Two ways to load FXML; why is one preferred over the other? - java

I am looking for feedback on why we should use one method over the other as far as loading and displaying new FXML stages.
Most of the time, I see tutorials and such that show the loading of a stage done from a separate class. However, it can also be done within the FXML file's controller itself, and I personally see that way as being a bit cleaner and more manageable.
Consider the following Main.java class:
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
// Method 1:
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout.fxml"));
loader.setController(new LayoutController());
stage.setScene(new Scene(loader.load()));
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
This seems to be the prevailing method. It creates the contoller and sets the Scene and then shows it.
However, if we change the start() method to this instead:
#Override
public void start(Stage stage) throws Exception {
LayoutController controller = new LayoutController();
controller.showStage();
}
and move the FXML loading code into the LayoutController constuctor, the result is the same:
public class LayoutController {
#FXML
private Label label;
private Stage stage = new Stage();
public LayoutController() {
// Method 2:
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout.fxml"));
loader.setController(this);
stage.setScene(new Scene(loader.load()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void showStage() {
this.stage.showAndWait();
}
}
The benefit I see here is more separation between the view and logic. Everything about the LayoutController and its associated FXML file are contained in one place.
So my question is this: what is wrong with the second approach? I am assuming it is not the standard method for a reason, but I'm not able to see any drawbacks.
Would a question like this be more suited to Code Review? I'm not really asking for opinions, as there seems to be a general "rule" that the first method be used.

In this case there is not much difference.
For larger programs the second approach is undesirable tough:
It violates the single responsibility principle:
The class is responsible for:
Creating the scene
Creating the stage
Displaying the stage (and even worse it does this in a way that may interfere with other logic (showAndWait))
It's the controller of the view and may become responsible for handling several events
Furthermore the way the class is designed in a way that prevents the responsibilities from being moved to other classes without issues.
In a larger program you likely want to create a class that manages passing data to views, arrange windows or display the view as part of a scene, ect. The second approach is ill suited for this.
Additionally it makes if harder to not repeat yourself. Unless you move the logic to a common supertype, you also need to implement the logic for showing the scene in every controller class. Repetition of the same or similar code results in code that is hard to maintain.
Note: Using a single class for loading the fxml and for use as controller is not necessarily a bad thing, but you should go with the Custom Component approach presented in Introduction to FXML.

Related

Java: keyPressed events not firing when game is opened through application class

I'm making a game in java, and I want to extract it into a runnable Jar. To make this possible I made this class:
public class AfterglowApp extends Application {
public void start(Stage primaryStage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(AfterglowApp.class.getResource("afterglow.fxml"));
Pane root = fxmlLoader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(AfterglowApp.class, args);
}
}
In my FXML controller I have a keyListener that triggers on keyPressed for the anchor pane in the FXML file. This works fine when I run it as an FXML application through Eclipse, but when I run it with the application class, no keypresses register (it still registers mouse clicks as normal though).
I have a feeling that the anchor pane of the FXML file loses focus, and that that's what's causing it to not register key presses, but I don't know how to fix that (if that even is the problem).
I realize there's probably something dumb I'm missing, but any help would be much appreciated.
I was correct, it was something stupidly easy. All I had to do was to call
.requestFocusInWindow();
on my anchor pane after the app started. Sorry for wasting the time of everybody who read this. I'll leave the question up in the unlikely event that somebody else gets themselves in a similar situation.

View related commands in MVVM application

I was inspired by article Decouple the View and its behavior to create a testable UI and wanna rework my JavaFX application with MVVM pattern.
(source: s-msft.com)
.
Ideally View Model should be View-independent and be testable as usual Java class. Also MVVM uses Command conseption to change View Model from View. So Command implementation is a part of View Model implementation and may be simply tested.
The questions is how Command should be implemented if it's result is a View changing? E.g. in View_1 I have a button after pressing on it a new View (e.g. View_2) should be created and shown. Should such Command be a part of a View and not be testable at all?
I think command could be testable. I'm not sure how exactly are you going to switch views, but AFAIK in JavaFX there is something like a Scene and Stage which is parent for scenes, is that right?
So in your command you pass the Stage as a dependency and on execution you set it a new scene.
And you can easily test it in unit tests - you can mock Stage and check if command passes correct scene to it.
I don't remember exact details of JavaFX but I would implement it like this:
class SwitchSceneCommand {
protected Stage stage;
protected Scene originalScene; // you can keep original scene if you want to have some undo-redo functionality
protected Scene newScene;
public SwitchSceneCommand(Stage stage, Scene originalScene, Scene newScene)
{
this.stage = stage;
this.originalScene = originalScene;
this.newScene = newScene;
}
public void execute()
{
this.stage.setScene(this.newScene);
}
}

MVP, JavaFx and components references

I've studied all popular GUI patterns - MVP,MVC,MVVM and finally I decided to implement MVP (Supervising Controller). So I have the following OBJECTS(!). Stage<-View<->Model. It's important Stage!=View, it is another object. Between view and model data binding. Besides I have a presenter(controller) which handles all events and works with view and model, so View<-ViewInterface<-Controller->Model.
The problem is now how to get references to labels, textAreas etc in view. Javafx allows to use #FXML annotation to inject these components to controller. However, using MVP I need these components in View, as all logic for view is in View and I don't need them in controller. The only solution I know is:
public class MyView{
private Button button;
public MyView(){
...
button=(Button) root.lookup("#myButton");
}
}
That is to get references by their ID. However I don't like it. Or I do something wrong or I understand something wrong but I think a better solution exist. Please, help me to find it.
JavaFX has been designed to work with the MVC pattern. Hence it is much easier to use MVC than MVP. In MVP Presenter is responsible for formatting the data to be displayed. In JavaFX, it is done automatically by View. Here's a quick overview of JavaFX MVC:
Model - the domain data / data structure that you work with in your application (e.g. Person, Employer, Coursework, etc)
View - the UI definition of the application and its Model. The preferred way of creating a view is via an FXML file, which is essentially the View in JavaFX MVC.
Controller - the bridge between Model and View. The code is typically isolated in XController class (where X is the name of the FXML View). The instance of Controller is automatically injected by FXMLLoader or can be done manually in case you require a custom Controller. The Controller class will have access to UI (View) elements in order to be able to manipulate different properties and also the Model, so that it can perform operations based on the UI (View) input.
To sum up, in JavaFX you don't need to have class View, the View definition should be entirely in the FXML file. All UI elements should be injected with #FXML into your Controller class. If you absolutely have to use MVP, then AWT/Swing or MVP4j - http://www.findbestopensource.com/product/mvp4j might be a better option.
For more detailed explanation please have a look at the official Oracle tutorial for JavaFX: http://docs.oracle.com/javase/8/javafx/get-started-tutorial/jfx-overview.htm
If you require help building UI using FXML: http://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction_to_fxml.html
This tutorial covers basics of MVC in JavaFX and how each component communicates with others: http://code.makery.ch/library/javafx-8-tutorial/part1/
As an Android developer, I always use MVP pattern in my applications. MVC compared to MVP seems so old to me, so when I started working on a new Java app, I felt a little bit lost.
Here there is my solution:
Initial steps
In fxml files create the UI, without specifying a controller, because you don't need one.
Create the Java interfaces (IView, IPresenter and so on..)
Implement the IPresenter interface in the Presenter class, as you would do normally (do http requests, query a DB..)
Now the interesting part:
Adapting your view to MVP pattern
Let's see some code:
Create your GUI (for example a Main GUI) and implement your View interface
public class MainGUI extends Application implements MainContract.View {
public static void main(String... args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
//here we will load fxml or create the ui programmatically
}
//method from view interface
#Override
public void onServerResponse(String message) throws IOException {
//update the view
}
Now the last step:
Communicating with the presenter
To do this, we first create an istance of our presenter:
private MainContract.Presenter presenter;
public MainGUI() {
presenter = new MainPresenter(this);
}
this is, of course, the MainContract.View implemented in the MainGUI class
Now we have to get a reference to the view components
private ComboBox<Double> mySimpleList;
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("layout_main.fxml"));
Parent root = loader.load();
mySimpleList= (ComboBox<Double>) loader.getNamespace().get("mysimplelist_id");
...
primaryStage.setScene(new Scene(root, -1, -1));
primaryStage.show();
I prefer using fxml files instead of creating the ui by code, but the logic behind is identical.
Set the items
...
mySimpleList.setItems(ValuesFactory.getMyValues());
And the listener
...
mySimpleList.valueProperty().addListener(simpleListListener);
What is simpleListListener?
A simple ChangeListener, where we finally call a presenter method
simpleListListener = (ChangeListener<Double>)
(observable, oldValue, newValue) -> presenter.doTheLogic(newValue);
This is an easy scenario, but in principle this is how we can use MVP Pattern with JavaFX. I also understand that it isn't the definitive solution, so I hope that one day there will be more docs where I can learn more about this argument!
Let me know if I wasn't clear in some part of the code

How do I set up a JavaFX Control to be imported into Scene Builder?

I have a JavaFX control that is basically an amalgamation of several other JavaFX controls.
I want it such that the .jar file can be imported into Scene Builder so that it can be used like any other control. The closest analogy I can think of is when you make a custom control in C# and use it several times throughout several projects.
When I try to import the FXML file, it doesn't work. The control isn't treated as a single entity, and instead is basically just all of it's parts strung out in the FXML file.
What do I need to do with the FXML file, or the controller.java file so that the Scene Builder will be able to import the .jar, see the control(s), and allow me to import and use each custom control as a single entity? I've looked several places and even asked on Stack Overflow once before (though the answer I got was not the one for which I was looking, and have received no responses since), but nothing I've seen comes close to handling my issue.
The closest I've come has to do with this line in the FXML file:
<?scenebuilder-classpath-element /path/to/something?>
but I don't know what goes in /path/to/something
I know I can, in the initialization, simply add the control to the scene, but that is sub-optimal and something which I am desperately trying to avoid.
I was finally able to resolve the issue. After much trial and error and following the sample code that came from here, I discovered my problem was that I needed 2 classes for each FXML control group.
One to be the actual controller of the group, and another to be the object that would house the controller. I followed the code in the Unlock example and it was a godsend for helping me.
Basically it comes down to two files:
The object (which extends the type of the root node, in my case):
public class <InsertObjectClassNameHere> extends <RootContainerTypeHere>{
}
After that you need the controller class. This is with what I am most familiar, however I was still doing it wrong. This is what needs to implement initializable:
public class <InsertControllerClassNameHere> implements Initializable{
}
So for me the Object class looks like this:
public class DGCSDefiner extends GridPane {
private final DGCSDefinerController Controller;
public DGCSDefiner(){
this.Controller = this.Load();
}
private DGCSDefinerController Load(){
final FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource("DGCSDefiner.fxml"));
try{
final Object root = loader.load();
assert root == this;
} catch(IOException ex){
throw new IllegalStateException(ex);
}
final DGCSDefinerController cntrlr = loader.getController();
assert cntrlr != null;
return cntrlr;
}
/**
* Get the settings defined by the controller.
* #return controller defined settings.
*/
public ColorSettings getColorSettings(){
return this.Controller.getColorSettings();
}
/**
* Set the controllers color settings.
* #param CS Defined color settings.
*/
public void setColorSettings(ColorSettings CS){
this.Controller.setColorSettings(CS);
}
}
and then there is the actual Controller class.
So for a straight-forward answer,
you need to have a class that will be loading your controller, and you need to pass down from your controller to that class that with which you will be working (Or, you can simply keep the controller public and access it directly).

Thin controllers

I am getting my feet wet with javafx. This is what I am doing.
FXML Views
DI Controllers
Weld-SE Managed Services and Models
Trying to confine UI to FXML
Trying keep the Controllers thin
Problem:
While trying to code the UI, most static UI is confined inside the fxml. But there are scenarios where I find my self adding, removing, showing, hiding elements etc.
I find myself doing this inside the controller as fx lets me configure controller method in the view which it will call on a particular action / event. All this code deals with Dynamic UI building / manipulating and belongs inside the view layer. But, it ends up in controller making the controllers fat.
javafx provides javascript integration. This is one possible way to abstract that view manupulation code away. But this would add not so perfect javascript into the mix.
How would I abstract the code away in java or fxml so that I don't break the Thin Controller Paradigm ?
EDIT
#assylias
Agreed, I have thought about this and this way that java class and fxml together become a reusable widget. But then, how do I wire this into FXML. FXML doesn't understand anything but a controller. Let say I wire this view class into fxml using fx:controller and not name it controller. So I have something like this.
This view class has nothing but view manipulation code. Then I would create another controller class. But then I would expect to somehow fill the form data into this controller. This should only happen when the user has submitted the form. So in a way, I need to tell javafx somehow that UI manipulation request / event is different from actual data manipulation request / event.
Your thoughts, sorry if it was verbose. Tried to articulate it in as few words as I could.
I think the easiest solution to this is to remember that the controller specified in FXML is a view controller. It's purpose is to contain code to modify and update the view, not to contain traditional MVC controller code or business logic.
For example, in a project I'm currently working on, I'm using JavaFX with Akka Actors. The application is written in scala. The JavaFX view controllers contain any code necessary to modify the view. One screen contains a login form. When the user clicks the login button, the view controller simply creates a message containing the username and password, and sends that message to the actor responsible for doing business logic. If that actor determines there is an error then it will send a message back to the view controller, and the view controller can decide what sort of updates need to be made on the screen.
I've found that using akka actors with JavaFX greatly simplifies coding the application for at least two reasons.
Because using an actor system mandates sending messages between actors, there is a natural boundary between presentation code and business code. The messages that are passed back and forth form this natural boundary.
Using actors completely replaces the complexity of working with threads/tasks. It completely eliminates the need to code javafx.concurrent.Task's for long running processes.
How about putting your view manipulation code in Main Class ?
Main Class :
public class SampleJavaFXApp extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"SampleUI.fxml"));
Parent root = (Parent) loader.load();
Controller controller = loader.getController();
viewManipulationLogic(controller);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
// view manipulation logic
private void viewManipulationLogic(Controller controller) {
controller.getBlueButton().setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
System.out.println(" I am just about button!");
}
});
}
Controller :
public class Controller implements Initializable {
#FXML
private Button blueButton;
public Button getBlueButton() {
return blueButton;
}
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
//real data manipulation
}
}
cons : You need getters for all Nodes u want to manipulate , in controller class.

Categories

Resources