It was was mid 2013 when i first tried to run the JavaFX samples under OS X, in fact, there’s still a draft post around here in which i wanted to describe the steps necessary to run that stuff with ant and how to use Maven to build an app.
Well back then i played around a little with JavaFX 2.1 but all that fiddling around including jfxrt.jar and such wasn’t much fun.
1,5 years forward i’m sitting in various conference rooms in San Francisco, enjoying JavaOne 2014 sessions.
I’ve seen two interesting JavaFX 3D sessions with respectively bye Sean Phillips and especially inspiring “Swing Away! Move to JavaFX 8 and the NetBeans Platform” by Gail and Paul Anderson.
My personal background: I’ve been creating Java Swing applications since 2004 now and we have one very graphic heavy GIS application and also many form based stuff, so there’s some background developing Java client applications.
I got myself Pro JavaFX 8 by Johan Vos, Weiqi Gao, Stephen Chin, Dean Iverson and James Weaver as well as Javafx Rich Client Programming on the Netbeans Platform by the Andersons, the later one didn’t arive in time for my experiments, so review here.
So give something to code. Why not reusing the topic from my series of posts Developing a web application with Spring Boot, AngularJS and Java 8, which had gotten quite some attention of the last months.
The app is build around a REST api that is currently used by an AngularJS frontend for the webpage.
My goal was to create a client application that:
- Displays all biking pictures, shuffles and flips them
- Displays all bikes and makes them editable
- Displays all gallery pictures (and in the future, also allows to upload some)
That gave me the following topics: Getting and processing JSON (i could have used Jackson again, but i decided for JSR 353), creating a basic JavaFX layouts using Scene Builder and connecting it to stuff, loading other stuff in the background so that the GUI won’t block, show progress.
The code for the this app named “BikingFX” is on github: github.com/michael-simons/bikingFX.
That is what it looks like:
The pictures in the upper row are randomly selected and flip around with a nice transition, the pictures in the table are lazily loaded. As added bonus, some table cells use specialized input fields (Date- and ColorPickers).
ProJavaFX gives you a real good start with JavaFX. At first, the authors give a brief history about JavaFX (which i find quite interesting) and than starts with the coding. The examples are all based on NetBeans projects and are available online, they work as advertised and are thoroughly explained in detail. Very nice.
All the topics i was interested are there: “Getting a Jump Start / Creating a User Interface”, very important “Properties and Bindings” (make sure to read this, Properties are the based thing that could happen to Java client GUIs, no more need for JGoodies, sorry; and Bindings are a great way to control computations and such), than “Collections and Concurrency”.
The chapter about UI Controls is missing some information about editable cells, but that can easily be retrieved from the API.
Overall, the book ProJavaFX 8 is a joy to work with. Didn’t notice any errors so far, the language is good, really nothing to complain.
I will show you some aspects of the little application, i’ve developed:
- Getting JSON data into the client
- Observing stuff
- Using background services
- Present and edit stuff in tables
- Deploying
- Mobile?
- Conclusion
It took me approximately 24 hours in total to get there.
Getting JSON data into the client
JSON data for my application will be from an online api so that must run in the background and not in the JavaFX application thread. The heart of the javafx.concurrent framework is the Worker interface with it’s three abstract base classes, Task<V>, Service<V> and ScheduledService<V> from which i use Task and ScheduledService in my program.
Retrieving lists of stuff in my application is a one time Task and so i used just that:
Notice the use of new Java 8 features, namely the ObjectFactory as a functional interface and the actual call() method where a method reference for a method on a concrete object is used to call constructors as factory methods that use the JSONP api like this:
The task provides no public constructor but a static getter that handles all the stuff to instantiate an ObservableList and fill it with stuff when the API call is finished.
The observable collections together with bindings makes creating tables with dynamic data in JavaFX so sweet. Putting those things together is just something like this:
“viewGalleryPictures” is an attribute of “TableView
All layouting took place in Scene Builder (by the way, separating that program code from the actual layout in FXML is by far the best solution for any GUI builder and one that actually works without constraints on source code).
In the snipped above you notice a constructor method reference. That stuff is really powerful and I hardly know what i did without it before Java 8. It allows changes like this “Replace specialized task with a generic one” and this.
Observing stuff
You can literally observe everything in a JavaFX application.
First of all there all the nice observable collections, in my case for example a list of BikingPictures that are loaded via the above JsonRetrievelTask. If they are finished loading, it’s time to actually load the image data and display it, so i observe the content of the list:
The event actually contains a list of changes but here i am only interested wether the list has actually elements or not.
There is also an HBox inside the root layout which is inject as bikingPicturesContainer into RootController. HBox is container element that lays out its children in a single, horizontal row and is used for the header of biking pictures in my app.
I observe its width with a listener (which would have been written as an anonymous, inner class pre Java 8):
Every time the main window gets resized, the listener is notified and uses #loadPictures to randomly select entries from #bikingPictures and then load them using an Image. I could have used an ImageView, but i wanted to observe the progress of the ImageLoading as well so that i can but a ProgressIndicator as a placeholder in my container and swap it when the image is ready:
If i had used the URL constructor from ImageView i would have ended up with an unresponsive UI, as it loads the images on the JavaFX application thread.
Using background services
Tasks are things that are usable once, services are for reoccurring tasks. For every execution they must generate a new task and so does my FlipImageService. That service is actually a ScheduledService and without much ado one can #start() it and has a periodically task.
The special service selects and loads one of the available pictures (that are does that aren’t showing at the moment):
loads an Image (this time immediately, notice the last boolean parameter):
If this succeeds, it picks on the JavaFX application thread one of the elements in the HBox mentioned above and exchanges it’s contains with a “flip” animation that consists of two ScaleTransitions:
Present and edit stuff in tables
Many applications deal with presenting and editing data in tables, so does this application.
All my beans are perfect JavaFX beans, that is: Their attributes are implemented as Properties, with final value getters and setters and corresponding xxxProperty() assessors, for example take a look at the Bike class.
To get them into a table is just a matter of few lines:
Given
@FXML private TableView<Bike> viewBikes; @FXML private TableColumn<Bike, String> viewBikeName; @FXML private TableColumn<Bike, Color> viewBikeColor; @FXML private TableColumn<Bike, LocalDate> viewBikeBoughtOn; @FXML private TableColumn<Bike, LocalDate> viewBikeDecommissionedOn; @FXML private TableColumn<Bike, Integer> viewBikeMilage; |
and fill like so
# Get an observable list of bikes that will contain them when loaded final ObservableList<Bike> bikes = JsonRetrievalTask.get(Bike::new, "/bikes.json?all=true"); # Set this as the item source of the table viewBikes.setItems(bikes); |
Then, bind the properties of the JavaFX bean
viewBikeBoughtOn.setCellValueFactory(new PropertyValueFactory<>("boughtOn")); # and optionally configure the cell factories (or cell renderers for all the Swing people out there viewBikeBoughtOn.setCellFactory(LocalDateTableCell::new); |
Feels way more natural than the “old” Swing Tablemodels.
The dates of a bike and the color need some nice representation. That was quick to implement, thanks to the already existing components DatePicker and ColorPicker (Find exhaustive information about most components available in ProJavaFX). It’s straight forward to use them as table cells:
You’ll notice that the constructor takes a column parameter. I need that for two things: Bind the editable property of the column to the editable property of this cell (with that solution, all cells are either editable or not, this cell implementation cannot be used for individually editable cells). The component is the mentioned DatePicker, which also is instantiated together with a custom format inside the constructor.
The actually rendering is done by update item. This method can be called very often so it’s important that this part is fast, therefore the stuff gets initialized inside the constructor. The docs for TableCell clearly states that calling super.updateItem is a must, also setting text and graphic to null if the cell is empty. The rest is just logic to use just a text when the cell isn’t editable and the date picker if it is.
Going back to the specific constructor signature: It can be used as a Callback for TableColumn#setCellFactory(LocalDateTableCell::new). The callback is called every time a new cell is needed. Doesn’t the constructor reference looks much nicer than a factory class or anonymous inner class implementation of the interface linked above?
At the moment, the server side is missing update features for decommissioning bikes and the bike color but i can add respectively set the current mileage of a bike, so i implemented that.
As i mentioned above, the Beans are all full JavaFX beans so any change in the TableCell is directly propagated to the bean itself. Therefor i’ve registered my handler to the properties i’m interested in on every bean whenever the list of bikes changes like that:
That is also a nice example of how to traverse the list of changes to a given collection.
The actual handler, MilageChangeListener, looks like this:
Again, what we see here is a task being spawned when the value of the cell changed. That task calls the rest API and updates stuff by calling a protected REST method, setting the appropriate authorization on the connection. Also shown here is how to POST body data to an endpoint. The connection must be told to do in- and output and than output can be written to the connections output stream. Again, i use the JSONP api.
What i actually don’t like about that solution is the fact that if the entered value is wrong, i cannot reliable reject an invalid change and the value stays in the table cell and the bean. If i would set the value back to the old value, than the listener will fire again immediate.
Notice two additional things:
Those are functional interfaces which are implemented by RootController#retrievePassword and RootController#storePassword and again, passed as method references when instantiating the listener in line 150:
final MilageChangeListener addMilageController = new MilageChangeListener(this::retrievePassword, this::storePassword, this.resources); |
Pre Java 8 you would have to either pass a reference to the full controller around or introduce some more or less useless interfaces.
Deploying
After all that years, i’m still a big fan of Maven. The memory of projects deployable only on one machine (when the moon is full etc.) still hurts. When i tried JavaFX last for the first time, that wasn’t actually going well, but those problems are gone as well. If you like, NetBeans creates a Maven JavaFX project for you that works right out of the box. For development it isn’t even necessary to add the jfxrt.jar as dependency, but the Maven compiler plugin needs it. Luckily it is contained now since some Java 7 version in the JRE/JDK and it is as easy as that:
to add it. No more mudding through system dependencies and such.
It has become remarkably easy to create single-jar applications and also native bundles with JavaFX and maven. The above configuration just does this, creating a single-jar (which is really small, with the one dependency (JSONP api and implementation) not much above 120k). I’ve added a second profile called installer which get’s activated during package phase with the property -DcreateInstaller=true and creates installer bundles inside target for the current platform.
That works reliable under OS X, Windows (7) and Debian, tested so far. The bundles for OS X gets a lot bigger than for Windows as the javapackager includes the whole JDK instead of the JRE on that platform so what was just about 100k becomes a hefty 170M. Anyway, that are the steps in the right direction.
Mobile?
With the JavaFX 8 ensemble hitting the Google Playstore just this week and being available in the Mac App Store for quite some time now i was hoping there is a relatively easy way to get this thing running on either one of my Android or iOS devices, but as i used plenty of Java 8 features, that isn’t possible at the moment. Neither RoboVM nor the JavaFX on Android community support Java 8 at the moment, but I’m sure that’s gonna change, especially in the light of the announcement that RoboVM and LodgON will team up to bring JavaFX to both mayor mobile platforms: JavaFXPorts.
Conclusion
I think I’m doing that stuff long enough to know when something feels just right. Apples swift is a nice thing if you only want to do Apple related stuff, so is the Android SDK.
But with my language knowledge of Java i can truly achieve “write once, run everywhere” using JavaFX. JavaFX compared to Swing is a real breath of fresh air and also, really needed. The controls looks great, even with their default theme and I didn’t even get started to skin them using CSS (which is beyond me, you just need to look at this site ;)).
If you’re into that stuff, get yourself a copy of ProJavaFX 8 for a solid and comprehensive overview of the terminologies and technologies used. If you’re assignment is to create a form based applications with a lot of dialogues, forms and menus i recommend the Javafx Rich Client Programming on the Netbeans Platform guide. As always, read the API, those docs are full of information and actually useful examples (for example on who to watch progress on a task from the platform thread). Last but not least there’s Oracles Getting Started with JavaFX with a lot of working examples.
Writing this application and this post gave me the same feeling of having made the right decision many years ago betting on Java as writing that post and the application. Java looked dated just before the Sun acquisition but that changed a lot and this years JavaOne motto “Create the future” seems actually be possible.
9 comments
Consider using the JAX-RS 2.0 Client API instead of HttpURLConnection… 🙂
Thanks, Bruno, for your feedback, very welcome.
I thought that was usable only in “enterprise” applications, but a quick search gave me that result http://docs.oracle.com/javaee/.....m#BABJCIJC Sweet. I’ll try this out, thanks!
Hi Michael,
Great post, thanks! Would love to see an update using Bruno’s suggestion.
Thanks, Bob, for your kind feedback.
Yeah, time and such 🙂 Working on it. Last weekend i added some charting which is really easy to do, then got a hint i’m nesting lambdas too deep, worked on that… Check it out if you like, it’s already on github.
Gotta love the Streams …
jsonReader.readArray().stream().map(objectFactory::createObject).collect(Collectors.toList());
Nice… 😉
Last time I did a Swing based application, very busy, multiple tabs, read-ahead dynamic sql results, very spiffy was 2002. Now I need to write an app that has bi-directional updated controls and has meters, waterfalls, and spectrum graphs all coming asynchronously through a UDP connection. Does this sound like an issue?
Very nice write-up!
Thanks for your kind feedback, Walt.
No, actually, i don’t think so…
All the nice bidirectional bindings are way easier to achieve with the new property system than through old bean bindings or with the help of external jars.
Have a look at FlexGantFX http://flexganttfx.com or CalendarFX http://www.calendarfx.com
I did try ensemble8, very impressive. When I said spectral, think of a heart monitor except much faster. You know the static on the AM broadcast bands, radio stations (630-1170 or there about in frequency) think of visualizing the static in a display widget. I could send you a jpg of what I mean if you can’t visualize it, but I don’t know how to do that. I did order those two books you mentioned, already had Java 8 by Example. Back in the mid-80s through early 90’s I did Windows and OS/2PM development. The way to do scrolling seamlessly was to scroll the picture down one pixel width and invalidate the top row of pixels. This will send a paint event for the region one pixel width across the control l->r. I don’t see examples of that having to be done any more. Is Prism smart enough to do that for you?
Thanks,
Walt
Hi Walt,
no, i surely don’t mind… I hope it’s ok if I’ll answer here…
I don’t think you have to scroll the pixels yourself, just go with some kind of animation / timer. I guess it should be pretty much the same as with the .NET app screenshot.
I am a backend guy myself, but i have developed some large Java Swing Gis Application from 2005 on which is still actively maintained for one of our customers. Especially when we started it people always where like “can’t be done with Java / Swing, its slow yada yada yada”… It’s far from being slow and the slow parts are mainly getting *lots* of geographical stuff from a database.
Regarding your screenshot: You can style JavaFX anyway you want, have a look at the FXyz sampler here https://twitter.com/jperedadnr/status/569601218785509376 or https://github.com/FXyz/FXyz/releases. In the later link you can download and run it…
Regarding the progress bar thingy from your screenshot: I guess it’s just a slider, not a progress bar. Setting it’s value to an arbitrary number inside the allowed range isn’t a problem at all.
If you have any questions, just drop me a line!
Post a Comment