JavaFX: Reusable Components and Observables

1 Model + 1 Controller + 1 Template=Multiple Objects

Gregory ‘Grey’ Barkans
6 min readSep 26, 2018

This article assumes basic familiarity with JavaFX. For those that are completely new to JavaFX there are helpful introductory documents provided by Oracle.

The goal of the article is to create an fxml template with an associated controller and model that can be instantiated multiple times.

All of the code for the entire project is found on Github:

This past week I was forced to plow through a typical (read mundane) computer science assignment. Create a POJO “model”, and use it in a separate “view” class (aka a Scanner System.in/System.out loop).

Let’s be honest, if you’ve been programming for 5 years (3+ with Java) and the only thing holding you back at this point is a piece of very expensive paper to prove it, this task doesn’t exactly excite you.

I find working within the confines of a strict grading scheme is sometimes more of a constraint than those elicited in the real-world. Quite simply, these assignments are hardly bound by the outcome of a unique solution space; rather they’re bound to a specific implementation that’s easy to grade.

The goal then is to make the grader dislike giving you the A, as they will have to actually read and understand code that is far from a cookie-cutter solution.

Enter a conniving plan:

  • A model that can be used plainly yet also observed
  • Implement a JavaFX GUI with a reusable FXML and Controller for the model
  • Implement the required System/Scanner loop running in a separate thread — changes manually typed here will update the GUI and vice-versa

The last requirement, although odd, provides an exciting additional challenge.

Model

The model in the assignment is a transit ticket-vending machine. For the purposes of this article, I’ll only explore a single property: selecting a transit route.

Below we see that there’s an enumerated type ROUTES as well as a member variable selectedRoute. The type SimpleStringProperty is from the javafx.beans package and is used in order to allow a property to be observed. However, observable properties are agnostic of JavaFX and as we see, a standard getter is defined that returns a plain String.

Further, the model is actually a common POJO pattern called a ‘JavaBean’, which is a very standard design pattern. JavaFX packaged its own ‘beans’ as an improvement on the existing one, but it is not tied to JavaFX. One could use javafx.beans in another GUI framework (ex Swing).

There are a few helpful discussions talking about these points on SO:

Model Implementation

Below is full implementation of this model:

There’s really only two things to note here:

  • setSelectedRoute is privately overloaded for convenience
  • Properties use get and set methods to assign values

View

The point of this article isn’t to dive into FXML or CSS — Instead I want to demonstrate how to create multiple Objects from the preceding Model that all use one template and one controller.

The design will be extremely simple: A TabPane with 2 Tabs. Each tab is a view for a separate ticket vending machine (ie: a SlotMachine).

Things to note about the FXML:

  • fx:id's are assigned to buttons and text that will be bound to model data
  • A controller isn’t included in the template — we’ll set that up explicitly

Controller

Creating a controller for the above view and model is relatively straight forward. Importing javafx.fxml.FXML allows for use of @FXML annotation, which binds to an fx:id as follows:

@FXML Text txtSelectedRoute ← variable name must match fx:id

The SimpleStringProperty is then used to “listen” to changes in the model:

getSelectedRouteProperty()
.addListener((obs, oldRoute, newRoute) -> {
// update text here
});

Button event handlers will update the model:

btnRoute1.setOnAction(
e -> model.setSelectedRoute(...);
);

Given that the fields annotated with @FXML are injected at runtime, a clever reader may ask “are they accessible in the constructor”? The answer is no, and for that reason, an FXMLLoader is used to “wire up”controllers to their views. This loader will inject the proper references, and afterwards call an initialize() method.

Therefore the initialize() method is where the above listeners and action handlers will be implemented.

For more information, here’s a helpful thread:

Controller Implementation

Binding Everything Together

At this point there’s a model, view and controller. All that’s required is to setup a scene with everything glued together properly. However, there is a gotcha with respect to resource loading. It will also become clear why the controller was not included directly in the FXML template.

An FXMLLoader is used to load fxml templates and bind controllers. Given that the controller class in this example accepts a model in its constructor, care has to be taken regarding the order of operations.

  1. Instantiate models
  2. Instantiate controllers with reference to models
  3. Load FXML with respective controller

Here’s some pseudo Java code doing the above:

Model mOne = new()
Model mTwo = new()
Controller ctrlOne = new(mOne)
Controller ctrlTwo = new(mTwo)
FXMLLoader loader = new ("view.fxml")Tab tab1 = new()
loader.setController(ctrlOne)
tab1.setContent(loader.load())
Tab tab2 = new()
loader = new("view.fxml")
loader.setController(ctrlTwo)
tab2.setContent(loader.load())

Did you notice the big “gotcha”? The loader was not re-used but instead re-instantiated. Failing to take this step will leave a nice compilation error about how “the root was already set”.

An important call to note is loader.setController(). This call explicity allows one to bind specific controller instances to specific instances of a template. Hence why the controller was not referenced directly in the FXML template.

In this example only two components were created. In more advanced cases (for example many components), FXMLLoader offers a method setControllerFactory, which is documented below:

Separate I/O Thread

This last step is kind of quirky and only arose from me “bending” around the rules of the original assignment. I will demonstrate how to invoke a separate thread running a command-line interface that works with the same models as in the GUI.

The code for the CLI is found here, and is outside the scope of the article.

In the main class, after the JavaFX code is rendered, start a daemon thread. The documentation states:

The Java Virtual Machine exits when the only threads running are all daemon threads.

which means that if the GUI is closed, the application (and CLI) will also exit.

Below a thread invokes the method runCommandLine from the MainCLI class.

MainCLI cli = new MainCLI(smOne, smTwo);
Thread cliThread = new Thread(cli::runCommandLine);
cliThread.setDaemon(true);
cliThread.start();

The result is a model synchronized between I/O and GUI events:

Wrap Up

This article demonstrated how to “wire up” a reusable MVC component with JavaFX. It also demonstrated how to work with the very same models used in the MVC component on a separate input/output thread.

The major topics discussed where:

  • JavaBeans Properties
  • Observing a JavaBean Property
  • Explicitly binding a controller to a template with FXMLLoader

The entire source for this project lives on Github. There’s also an executable jar of the example that can be downloaded here.

I write articles regarding explorations in computer science and web development on a ~weekly basis. Read Code Something Weekly: How and Why for more information:

--

--

Gregory ‘Grey’ Barkans

I’m a software engineer between Hamont ← → ATX that’s mainly interested in technology and philosophy. I used to spin DJ mixes as well. vapurrmaid.ca