ImageView fails to load shortened url, only sometimes - java

I've observed a weird behavior (bug?) while loading images with ImageView providing a shortened link but won't happen with every image. I've tried other shortened images with success.
Here's an MVCE that just loads a blank white window. The target url is:
https://images-na.ssl-images-amazon.com/images/G/01/kindle/merch/2017/ECHO/GW/D_GW_BLU_2UP_1500x300.CB536733694.jpg
Which does load.
public class ImageTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ImageView v = new ImageView("http://bit. ly/2liTJCa");
Scene s = new Scene(new StackPane(v));
primaryStage.setScene(s);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
Note I've broken up the bitly link, since SO won't allow me to post it otherwise, just remove that extra space when testing.
EDIT: I managed to see an exception by calling img.getException() to the underlying Image object:
com.sun.javafx.iio.ImageStorageException: No loader for image data

Related

Issue with 2 main Methods Java And JavaFX

I have currently 2 classes, both with main methods, inside one project. The first class looks like:
#SpringBootApplication
public class Application implements CommandLineRunner {
public void handle(String s) {...}
}
and the other one is a simple JavaFX starting class:
public class MainMenue extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("/Sample.fxml"));
Scene scene = new Scene(root);
primaryStage.setTitle("Hello World");
primaryStage.setScene(scene);
primaryStage.show();
}
}
My plan is to start the javafx App to have some buttons and all the fxml feature, and I would like to start the other application with a button event. That already works fine. BUT I have to use some methods from the normal Java application from the JavaFX app. To be more specific, there is a handle method which takes String as parameter, and I would like to write the String in a Textfield in JavaFX and by clicking a Button, the method "handle" from the normal Java application should get done.
I couldn't find any solution to solve this. Making the class Application extends Application AND implementing this interface isn't working fine.
Do you have any solutions or other ways to do so?
Thanks in advance!

unable to load a javafx scene

I have a problem with loading a file. I'm trying to load the scene "areaView.fxml" with the following code:
public class View extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/resources/areaView.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}}
But when I launch I get an InvocationTargetException..
For information i'm on windows and the absolute path of the file is: C:\Users\pierr\Desktop\Yves\resources\areaView.fxml
It's probably a stupid mistake but I've been stuck on it for quite some time.... I'm almost sure it's related to the "/" but all my attempts to fix it have failed...
Thank you in advance
EDIT :
Here the full stack tace
EDIT n°2
Very strange thing when I test this:
System.out.println(getClass().getResource("").toString());
I get this : file:/C:/Users/pierr/Desktop/Yves/out/production/Yves/view/
When I would have imagined getting this: file:/C:/Users/pierr/Desktop/Yves/
If i move my file to file:/C:/Users/pierr/Desktop/Yves/out/production/Yves/view/ it works but in fact this solution is not satisfactory
By default your resources folder should be under projectName/src/main/resources.
So in your case: C:\Users\pierr\Desktop\Yves\src\main\resources\areaView.fxml.
In such case .getResource("areaView.fxml") should be sufficient.
Just keep in mind that if you move your fxml to a different folder or you call getResource from a different folder than src/main/java you will have to change the relative path that you pass to the method.

Can't edit non-static text area in Javafx Application class from other class

I'm trying to implement a GUI in JavaFX for a text-based game I've been making.
This part of the main class sets everything up:
public class Main extends Application{
#FXML
protected TextField input;
#FXML
protected TextArea output, inventory, commands;
protected static List<String> history;
protected static int historyPointer;
protected static String textToRead = null;
private Service<Void> backgroundThread;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("Console.fxml"));
BorderPane root = (BorderPane) loader.load();
history = new ArrayList<>();
historyPointer = 0;
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("MyConsoleFXGUI"); //Could later be changed so that the actual game title is displayed here.
stage.show();
I use a FXML-file generated from SceneBuilder and Main is the controller. It works well and when I tried to set some text to input through the initialize function, the text printed fine (but I have now removed that method).
The problem comes when I then launch my Game-class and try to print text from it to the text area "Input" in main.
I use this method in Main to set the text:
/**
* Called when the game wants to print something to the game
* #param message The text to be printed to the console.
*/
public void printGameInfo(String message) {
System.out.println("This method was attempted!");
output.setText(message + System.lineSeparator());
}
This method should work, the problem I have is that I don't know how to call it from the Game-class. Since the Main class isn't instantiated I can't call on a Main-object and I can't make the text area static as that doesn't work with JavaFx applications.
So how do I go about to call the "printGameInfo" from a separate class to set some strings to a text area?
Thanks a lot!
The Main class definitely is instantiated or how should anything be able to call its start method which is not static? When you instantiate your Game class you could pass it a reference to your Main class instance. Nevertheless this is an awfull software design.

Can i create various scene in diffent class file and switch between them in javafx

Please am new to javafx. the tutorial i watched switched between two scenes that was on the sam class file. I am thinking if i have 10 scenes that will make the code very lenghty. Can i create each scene in a diffrent class file and switch between them in. How?
You sure can, Scenes are regular classes after all.
I've created a simple example.
One thing to note: I deliberately did not extend Scene, because that is usually unnecessary. You can compose the scenes anywhere you like (in dedicated classes, methods, ...). In this example I used two small factory classes.
public class App extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
primaryStage.setScene(SceneAFactory.create(primaryStage));
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(App.class, args);
}
}
public class SceneAFactory
{
public static Scene create(Stage stage)
{
Button button = new Button("Switch to B");
button.setOnAction(event -> stage.setScene(SceneBFactory.create(stage)));
return new Scene(new HBox(new Label("Scene A"), button));
}
}
public class SceneBFactory
{
public static Scene create(Stage stage)
{
Button button = new Button("Switch to A");
button.setOnAction(event -> stage.setScene(SceneAFactory.create(stage)));
return new Scene(new HBox(new Label("Scene B"), button));
}
}
Keep in mind though, that depending on the task at hand, replacing just a part of the scene graph might make more sense than replacing the entire scene.

Java Form problems

I've recently delved into JavaFX as a C# developer. One thing I noticed in Java is that you're not spoon fed the way Visual Studio/Microsoft spoonfeed us.
So. When I was creating a form using the scene builder for IntelliJ Idea on JavaFX. I inherited "Stage" for my controller class and created a void called load that will load the instance of the scene from the FXML file. Therefore when I call load() from the Main entry point or anywhere it will load the fxml file and show.
LoginController frmLogin = new LoginController();
frmLogin.load();
The problem is that it works and it does't work.
Here's my code.
Main.Java
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
LoginController frmLogin = new LoginController();
frmLogin.load();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
LoginController.Java
public class LoginController extends Stage
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
this.show(); //The problem occurs here!
}
else
{
Messagebox.Show("Incorrect Login");
}
}
public void load() throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
this.setScene(new Scene(root));
this.setTitle("JavaFX GUI");
this.setResizable(false);
this.initModality(Modality.APPLICATION_MODAL);
this.show();
}
}
Here's a GIF of the problem.
http://i.imgur.com/0hOG76M.gif
I want to know why when I call .show() it shows a blank for?
Any help would be appreicated.
Solution
Don't inherit Stage in your Controller.
JavaFX will implicitly create a Stage for your application and pass it to your application (the primaryStage parameter in your application start method).
Sample
Here is a quick update which should work. Another alternative for this is to factor out the stage management as in James's answer.
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("JavaFX GUI");
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
. . .
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Aside: I am not sure what your MessageBox class is, but JavaFX 8u40 has a built-in Alert dialog box for standard message box style functionality, so that would be the preferred method to do that.
It looks like you have confused the different pieces that make up the application.
The FXML typically represents the "view"; i.e. the portion of the UI that is visible. It defines what controls are displayed and how they are laid out.
The controller implements the logic that is connected to (controls) the view. So it typically processes user input and updates the view in various ways.
A Stage is a window.
So, I don't think it really makes sense that your controller is a Stage. There are some scenarios where you might make a controller a subclass of a UI element, but those are somewhat advanced uses of JavaFX, and even then you would typically subclass a layout pane, not a Stage.
Here's roughly what happens when you call load on an FXMLLoader:
The FXMLLoader creates a hierarchy of Nodes (UI elements) corresponding to the elements defined in the FXML file
If the FXML file defines a fx:controller attribute in its root element, the FXMLLoader constructs a new instance of that class. It then injects any elements with fx:id attributes into fields in that controller instance with names matching the fx:id values. It also registers any event handlers mapping to methods in the controller instance.
The FXMLLoader's load() method returns the object corresponding to the root element of the FXML file.
So, in your code, you actually end up with two LoginController instances. You create one yourself in the start() method. You then call load() on that instance. That method calls load(...) on an FXMLLoader (via the really ugly static load method). Calling FXMLLoader.load(...) then causes the FXMLLoader to create an instance of the class declared in fx:controller. I'm guessing (you didn't show the FXML code) that class is also LoginController. So that is the second instance.
Now what happens, is that you get a reference to the UI element from FXMLLoader.load(). You put that in a Scene, and set the Scene in the LoginController, which - unusually - is a Stage. Then you make the Stage appear on the screen with show(). Note this happens in the instance you created in the start method.
When the user presses the button that has btnLogin_Clicked registered as its handler, the handler method is invoked on the controller instance: the one created by the FXMLLoader. That instance never had a Scene set, so when you then call this.show() it shows that instance of the LoginController (which, again, is a Stage). Since it never had its scene set, you see a blank window.
It's not actually clear to me what you intend with the call to this.show() in btnLogin_Clicked anyway. Assuming you thought this was the same Stage you had created from the start(...) method, that Stage is already showing.
The typical pattern is that you use the primaryStage that is passed to the start(...) method, and set a scene in that and show it. So you'd do something like:
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("JavaFX GUI");
primaryStage.setResizable(false);
primaryStage.initModality(Modality.APPLICATION_MODAL);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
and then the controller is just a controller: it just handles the logic:
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
// I don't really know what you were trying to do here
// but if you need a reference to the window containing the
// associated fxml elements, you can get it from one of those
// elements:
Stage stage = (Stage) txtUsername.getScene().getWindow();
//this.show(); //The problem occurs here!
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Typically what you want to do when the user has successfully logged in, is to display something new in the current window. The simplest way to do this is just to set the root of the current scene to the content of a different FXML file. For example:
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
Scene currentScene = txtUsername.getScene();
Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
currentScene.setRoot(root);
// resize window:
currentScene.getWindow().sizeToScene();
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Here Main.fxml defines the main application the user sees, having successfully logged in, and defines its own controller class, etc.

Categories