By Adam Carroll
In this blog post, I explain how JavaFX can be used to create the user interface for a cross-platform desktop application. This is all based on the real-life experience of building VocabHunter, an open source application to help people learning a foreign language.
In my day job at King, I work as a server-side Java developer and really enjoy the challenges that my work brings. At King, we get a lot of opportunities to learn new skills and develop professionally, and during my time learning about new Java technologies I became more and more interested in JavaFX. Combining this with my keenness and that of my colleagues at King to learn foreign languages, I started work on my open source project, VocabHunter.
In my previous article, VocabHunter — A tool for learners of foreign languages, I gave an overview of the system and talked a little about some of the technologies. Here, I go into greater depth about how the user interface was built using JavaFX.
VocabHunter is entirely open source, so please feel free to download the source code here from GitHub and to play around with it. In the spirit of open source, I hope that there are ideas that you can take from this and apply to your own projects.
Where appropriate, I have included links to the source code in GitHub to make it easier to follow along with the examples in the article. Since VocabHunter is a live project in active development, it is highly likely that files will change content, name, and location over time. To avoid problems, I have fixed all of the GitHub links in the article to a specific tagged version (version 1.0.16) of the source code. That way, the links will remain valid even after future work to refactor and improve the codebase. Of course, if you are really interested in the project, take a look beyond the tagged versions to see the very latest version of the system!
The VocabHunter user interface is built entirely with JavaFX running on Java 8. As such, it is cross-platform and looks very similar on each of the operating systems on which Java 8 runs. As with most systems, VocabHunter is designed to be easy to use, with the intention that it should be bright and visually appealing as well. It is worth bearing in mind that the user is likely to invest considerable time working with the system when analysing a long document so an effort has been made to provide visual clues to give the user a sense of progress. A user should know how much work remains to be done and where they are in the process. With all of this in mind, the user interface was built taking advantage of the rich library of components that standard JavaFX and external open source JavaFX libraries provide.
The following screenshots show the main screen layout highlighting some items of interest:
A user of VocabHunter may need to hunt through a lot of words to analyse a whole novel. To help reduce the workload, the system provides a facility to filter out words that are not of interest or that the user already knows. This is configured through a filter settings dialogue, as shown in the following screenshot:
Since the above settings dialogue is simpler than a lot of the rest of the user interface, I use it here for most of the examples. That said, it uses exactly the same JavaFX and libraries as the rest of VocabHunter so everything said about this settings dialogue also applies to the user interface in general.
Software Structure Overview
The Model View Controller (MVC) software architectural pattern was applied to the design and implementation of VocabHunter. One of the great benefits of this approach is the separation of concerns: we decouple the logic in the controllers from the view and the model. When we achieve this separation, we get real benefits in the maintainability and testability of the code. Sometimes this is easier said than done, and sadly it was all to common to see, for example, Java Swing applications with classes mixing up all three parts of MVC together. JavaFX by contrast gives us some tools that help us to make that separation real. These tools include an XML language for describing views, CSS for applying styles to those views, and support for connecting up controller classes to views and facilities for binding model values. I’ll go into more detail about these technologies later in this blog post, but first let’s return to the settings dialogue for the word filter.
The settings dialogue follows the MVC pattern, and you can divide up the components used to build it into the three main pillars of the architecture. I have excluded some detail here, so that we can focus on the most important components:
- MainModel — This model is shared with a large part of the VocabHunter user interface. For our purposes here, the most important properties are filterSettings (see the FilterSettings class) and enableFilters, a Boolean property for switching the filters off and on.
- FilterFileListModel — This holds the list of files of words to use as filters, as chosen by the user.
- settings.fxml — This is a description of the components of the user interface and their layout. In the next section, I discuss FXML files and their use in describing the view.
- main.css — A CSS for styles such as colours and font sizes. Again, take a look at the next section for more information.
- SettingsController — The logic that drives the filter settings dialogue.
FXML and CSS
It is common for a user interface to be built up as a tree structure of components within other components. A central panel might contain a bar with buttons on it at the top, which in turn contains the buttons themselves. These are all then laid out with particular positioning and spacing that will usually change to take advantage of the available space as the user resizes the window. All of this can be expressed in Java code, but JavaFX provides a better way to describe the view: FXML and CSS.
FXML is an XML-based language that describes the components and layout of a user interface. Here we will look at the FXML used for the filter settings dialogue, settings.fxml. Simplifying a little, you can see the following relationship between the actual GUI and the final part of the file:
Any component that you describe in the FXML file can automatically be created for you by JavaFX. Thus <Button fx:id=”buttonOk” … text=”Enable Filters” /> creates the “Enable Filters” button. There is no need to write Java code to instantiate the button object. This approach brings a lot of advantages over defining the components and layout directly in Java code, including:
- The structure of the GUI is implicit in the FXML document. In the example above, the button bar contains the buttons and therefore the <ButtonBar> XML element contains the two <Button> elements as children.
- A GUI with a complex nested structure that can be hard to understand in Java code becomes much easier to read in FXML, again because of the implicit nesting structure.
- The components contained in the FXML document can be directly instantiated by JavaFX. References to these components are then injected into controller objects where they are needed.
- In addition to being human readable, FXML can be parsed and manipulated by software. This makes it possible to use, for example, the Gluon Scene Builder (discussed later in this blog post) as a visual editor for the GUI.
- Putting the view description in its own format helps to separate the view from the other two parts of MVC.
Cascading Style Sheets are another tool in the JavaFX toolbox for defining views. They work well in combination with FXML to control styles, such as colours and sizes of the components in the FXML. I discussed this briefly in my first post on the King Tech Blog. To take a simple example from the VocabHunter filter settings dialogue, look at the title Choose the words you want to show. This title is in a bigger font than the rest of the dialogue and has some space around it. This font size and spacing is handled by the following CSS in the file main.css:
There is then a reference to the description CSS style class in the FXML. Putting this all together you get the following:
Controllers are standard Java classes that contain the logic that drives the user interface. In the case of the filter settings dialogue, take a look at the SettingsController class. The FXML document settings.fxml contains a reference to the controller, and then using this reference JavaFX is able to directly inject the components contained in the view. If you look at the class, you will see attributes for the GUI components such as text fields and buttons:
There are no constructors for the GUI components here: all of these components are implicitly instantiated when the FXML file settings.fxml is read and then the instances are injected into the instance of SettingsController. This is very powerful as it frees up the developer from creating the view objects but still gives the controller the access that it needs.
In common with user interface frameworks such as Swing, JavaFX uses the concept of a listener. Basically a listener is a bit of code that will be run in response to an event such as the user pressing a button. If the user clicks the Know button, part of the program needs to run to record the fact that the user has marked a word as known. This part of the program is a listener. The idea of listeners is adapted from the classic Observer design pattern.
Before Java 8, it was common to implement listeners as anonymous inner classes. This left a lot of boilerplate code and often made classes a little ugly and hard to read. Now, with Java 8, it is common to use a lambda expression or a method reference to express the listener in a more succinct fashion.
The following example shows how the Add Excluded Word List button in settings.fxml is assigned a listener:
Properties and Binding
JavaFX takes the idea of properties used in standard Java Beans and expands on it to make something more powerful for building user interfaces. With JavaFX, we have the new idea of a property which is observable: we can attach a listener to it so that we can run part of the program when the value changes. For example, a text input field has a value property so that you can add a listener to it to be notified when the value changes.
On occasion, writing listeners to handle all of the changes is more work than is needed. Sometimes what you really need to do is to be able to say “keep these two properties in sync” or “when the value of this property changes, update the value of this other property.” This is what the property binding mechanism gives us in JavaFX.
VocabHunter makes good use of this property binding mechanism in several parts of the user interface, and a good place to see a simple example is the progress tab. The progress tab is intended to give the user an idea of where they are in the process of analysing the document:
As you can see, there are lots of values on display, both in the pie charts and in the legends underneath them. Let’s pick one and focus on it. On the right-hand side pie (the Results chart), there is a line in the legend with the label Marked as Known. This is the number of words on which the user has clicked the Know button to signify that a word is not new to them and that they don’t need to add it to their study list. The value in the example, 1,149 Words, is bound to the corresponding value in the model. When the number of words marked as known changes, this value automatically updates. The property binding mechanism handles this without the need for a custom listener that waits for the model change and then updates the label value when the listener fires.
In our example of the Marked as Known property, the label that is shown is actually slightly more complex than the underlying model property. If you take a look at the model class, ProgressModel, you will see that the property is defined as follows:
private final SimpleIntegerProperty known = new SimpleIntegerProperty();
However, the corresponding label property in the view is a text string used here to display easy-to-read values, such as 1,149 Words. Clearly, there is a transformation taking place in the binding. This is one of the great features of the JavaFX property binding API: you can transform the value that is bound. In this case, you can think of this process as follows:
You can find the code that performs the property binding in this example in the ProgressController class.
One final benefit of property bindings I would like to mention is that they lend themselves well to unit testing. When values are transformed in property bindings, it is a good idea to unit test the transformations. For a simple example of this, take a look at ProgressModelTest.
Tools and Libraries
As with all software technologies, good tool support is important. Everyone has their own preferred Java Integrated Development Environment (IDE) and most of them have good support for JavaFX. Personally, I use IntelliJ IDEA, and out of the box it supports useful things, such as clicking through from FXML documents to Java classes. I also like its ability to create attributes in controller classes for view components. Take a look at your own IDE and see what you have available to you either directly or via plug-ins and I hope you will be pleasantly surprised.
No discussion of JavaFX tools support is complete without a mention of the excellent Gluon Scene Builder. This is an open source visual editor for FXML. As discussed above, FXML is an XML-based language for describing the components and layout of the user interface. As a format, it is quite readable and often I edit the XML directly in the IDE. However, it is useful to have a visual editor for certain tasks. I find it particularly valuable when trying things out quickly and for finding out what properties I have available for components and then testing out different values. It is also a great help when fixing layout problems. It includes handy support for CSS. The following shows the Scene Builder in use with the VocabHunter filter settings dialogue:
The IDE I use offers the ability to embed the Scene Builder. I started working like this, but have actually found that it works better if I run the Scene Builder as a stand-alone program. Have a try yourself to find out what works best for you.
JavaFX on Java 8 comes with a rich set of user interface components. In addition to this, there are some great open source component libraries, such as ControlsFX. VocabHunter uses the ControlsFX status bar component to help ensure that the user knows what is going on at all times.
User Interface Tests
The VocabHunter code is structured to make it as easy as possible to unit test it. There are lots of tests at all levels of the system. One important level is that of the user interface itself. VocabHunter includes a GUI walkthrough that really helps to catch problems before they get committed to the repository. The idea is to try out the basic functionality of VocabHunter in an automated test, clicking on buttons and working through a very simple session. All of this is made possible using the open source TestFX library. TestFX allows you to operate the user interface using a test ‘robot’ and verify the resulting state of the system. Take a look at the GuiTest class to see the details. If you would like to see the tests running on your own system, clone the VocabHunter repository and run the following command (on Windows, replace ./gradlew with gradlew.bat):
./gradlew :gui:test --tests io.github.vocabhunter.gui.main.GuiTest --rerun-tasks -PnoHeadless
You should see that VocabHunter starts and that the ‘robot’ then works through a simple session, clicking buttons, and using the menus.
Although it is fun to see the test process in action, in a normal build it is more convenient to run the tests in ‘headless’ mode where you can’t actually see the user interface. This is possible with TestFX, and in fact the GUI tests run for every full build. When a commit is pushed to GitHub, the full build (including the GUI tests) is run on the Travis Continuous Integration servers. If you are interested in the setup for this, take a look at the Gradle scripts, particularly build.gradle in the GUI module, the TravisCI configuration in .travis.yml, and the TravisCI job itself. It takes a while to set all of this up, but I think that the benefits in terms of catching problems early make the effort more than worthwhile. For more details of this, see the post I wrote on the subject, User Interface Testing with TestFX. Just as I mentioned for the rest of VocabHunter, please feel free to use the ideas here if you are interested in automating your own JavaFX user interface tests.
JavaFX is a powerful, modern, cross-platform user interface framework with a lot of features that help in the development process. I hope that this blog post is helpful to people considering working with JavaFX or those already doing so. All of the code for VocabHunter is open source and can be found on GitHub at https://github.com/VocabHunter/VocabHunter. Please feel free to download it and play around with it to see how it works and, of course, use the ideas in your own projects. If you would like to get involved in the VocabHunter project itself either by reporting and fixing problems or by contributing new code, please do so!
If you haven’t already read it, you might be interested in the first article I wrote for the King Tech Blog: VocabHunter — A tool for learners of foreign languages. There are lots of other interesting articles on our Tech Blog including some great stuff on open source. Have a look around and see what interests you!