Building Cross-platform UIs in Java
An overview of the "old-faithfuls", as well as a peek at some "up-and-comers" you might not have heard of yet
The focus of this newsletter is “Desktop Java” and a big component of “Desktop” is UI, so I wanted to include an article that summarizes the history and trajectory of Java’s cross-platform GUI toolkits over the years. First, here’s the short version: in the beginning there was the AWT, then came Swing, and finally came JavaFX.
This short version is missing a lot. It omits many alternative toolkits that have been developed, and are still used actively to this day. It also skips the context and motivations. For example, Why Swing? Why not just stick with AWT, or just improve it - is there something fundamentally different about Swing that made a rebranding necessary? Same for JavaFX. Why was it necessary to create a whole new thing - why not just add features to Swing?
Let’s unpack that “short version” a little bit more to help understand where we are, as Java desktop developers, and how we got here.
AWT
In his first-hand account of the origins of Java, Patrick Naughton tells how he revived some old Oak1 interfaces to X11 to make a mini-toolkit with components, and fonts, and buttons, and scrollbars, etc….
I felt the need to get something working in Oak that was wicked cool on a desktop machine. For the previous year, all we ever saw running in Oak was displayed on a television screen on the set top box simulator. So, I set to work writing a simple abstraction for a window system. I revived a bunch of old Oak interfaces to X11 on my desktop. I wrote a mini-toolkit in Oak which had components and fonts and buttons and scrollbars etc. I could then display little windowed applications written in Oak on a desktop computer. I ported a dynamic graph layout algorithm from C++ to my toolkit. I then wrote a simple HTML parser and renderer which could display our group's home page.
This mini-toolkit would evolve into the Abstract Windowing Toolkit (aka AWT), which was bundled with Java 1.0. Patrick’s description of how he created it gives you a window into the AWT architecture. It began as an abstraction over X11 components. To convert it into a cross-platform UI kit, they would later add bindings to the native UI toolkits of the other target platforms: Windows and Mac.
This architecture sounds good on its surface, but imposes some serious limitations. What if a UI component isn’t available on a particular target platform, or it behaves in ways that aren’t compatible with the other platforms? You end up having to reduce the toolkit functionality to the lowest common denominator.
This limitation led the engineers at Sun to imagine a new toolkit that they would build from the ground-up.
Swing
Swing was born out of the lessons learned from AWT of “what not to do”. Instead of building abstractions over the platform’s native UI components, Swing would draw its own components using 2D drawing primitives. This would allow it to overcome platform limitations. Since it no longer relied on the platform’s components, Swing could develop its own rich set of UI components without being handcuffed by the underlying platform’s components. It included a pluggable look and feel system so that it could be “themed” to look like the native platform.
Despite the fact that this was a bottom-up rewrite of AWT, Swing integrated well with the legacy AWT APIs. Swing’s base component class, JComponent, was a subclass of the AWT Container class, for example. In fact the entire Swing library integrated amazingly well into the existing AWT framework. This helped adoption as the APIs were familiar to AWT developers, and, in fact, transitioning an existing project from AWT to Swing didn’t require many changes. You could include Swing components in your existing AWT app almost seamlessly.
Swing was available as a separate set of jars in Java 1.1, and was included as part of Java 2 (J2SE). As of JDK18, it is still included as part of the standard Java distribution, and, as far as I know, there are no plans to remove it. It has proven highly resilient, even if it is getting a little dated.
JavaFX
Before getting into the “facts” about JavaFX, I want to share a short anecdote that serves as a sort of allegory for my feelings about it.
I’ve never been a “video game” guy, but a few years ago, I came across an Xbox 360 at a thrift store and thought, hey, this might be fun to play with the kids. Before checking out, I made sure to pick up a few games so we would have something to play. Among the games on the store’s game shelf was “Indiana Jones and the Emperor’s Tomb”. “That’s great!”, I thought. My son will love it. (We’re both big Indy fans). When I picked up the game box, I noticed that the artwork looked a little different than the rest of the 360 games. It was missing the “360” - it was for the regular Xbox.
I grabbed it anyway, since it was only a few dollars. “Maybe it’ll still work”, I thought. It didn’t. So now we have this Indiana Jones game sitting on the shelf next to our Xbox 360. The artwork on the box makes it look like a lot of fun, but we don’t have any device to play it on. Nonetheless, I’m glad that we have it, and am hopeful that the future will lead us to a place where, one day, we can finally play this game.
This is a little bit of how I felt when JavaFX was first launched.
JavaFX was touted as Sun’s next generation UI toolkit for building modern multimedia applications. It supported modern codecs for audio and video, which was exciting for developers of the aging Swing toolkit that had fallen behind on that front. It also incorporated a new scene graph that enabled higher performance graphics and animations. When I saw the demos, I couldn’t wait to plug this into my existing Java apps.
But, it’s not Java…
There was only one catch, JavaFX wasn’t Java! JavaFX allowed to you to build these fancy new applications in a cool new scripting language that looked like a declarative version of JavaScript. There were a few tutorials on how to integrate these into an existing Swing app, but they looked like kludges. So, if I wanted to use this great new UI kit, I had to learn a new language and it still wouldn’t integrate easily with my existing Java apps. So JavaFX would sit on the shelf of my mind, in the hope that one day I would have a device to play it on.
The initial release of JavaFX was in 2008, and two big factors beyond its control (to an extent) had already colluded make it irrelevant:
HTML5 had made the web browser the de-facto standard target platform for enterprise apps. The first draft was released in 2007, the year before FX’s release, but it had already been breeding in the wild since 2004.
Apple’s iPhone, also released in 2007, transformed mobile into the new hot platform that everyone wanted to target.
In the one or two years leading up to FX’s launch, web and mobile had completely changed the expectations for cross-platform GUI apps. JavaFX, the hot, new, cross-platform UI toolkit, didn’t run on the two most popular platforms: web and mobile.
I recall seeing a teaser demo from Java One (circa 2010 or 2011) where they showed a JavaFX app running on an iPad. It generated a lot of excitement and applause but I never saw it mentioned again.
Are you in or are you out?
When Oracle acquired Sun, in 2010, they decided to port JavaFX into Java, and roll it into the standard JDK. This was a welcome development to me and most of the community as it promised to make all of its new graphics features more easily accessible to our apps. Although I don’t think Oracle explicitly stated it at the time, there was an assumption that this direction would lead to Swing’s deprecation and that JavaFX would become the de-facto successor to Swing. This would not ultimately come to pass, however, as Oracle reversed course, and removed JavaFX from the standard JDK in JDK11, in 2018. It is still actively developed, and maintained, and some JDK distributions still include JavaFX2 for convenience, but it is not widely regarded as a successor to Swing, but rather as an alternative or a complement.
In present day 2022, despite all of its mixed signals and false starts, JavaFX has settled into its own niche, and has quietly grown into a very enjoyable desktop UI toolkit. It provides a fairly comprehensive set of high quality UI widgets, and it boasts a very nice GUI builder - perhaps the nicest Java GUI builder around - named Scene Builder. It owes a large part of its good development experience to the richness and stability of the Java eco-system in general. Great IDEs like IntelliJ and NetBeans make the development of long-term, large-scale projects quite manageable. Build tools like Maven and Gradle also make it easy to incorporate third-party libraries.
Unlike the wild west of Javascript UI frameworks, which use competition and disruption as catalysts for improvement (a new framework popping up seemingly every year to supplant the incumbent as the new hot framework), JavaFX’s improvements tend to be slower and more deliberate, which result in compounding improvements. If you learned JavaFX ten years ago, those skills are still applicable. Same, with component libraries you may have built for JavaFX ten years ago. They will still work (probably). The same cannot be said for the Javascript framework of the week. I’m not suggesting that the Javascript approach is wrong - it fits well into the ethos of “move fast and break things”. I am suggesting that there is a significant comfort in working in an ecosystem like JavaFX where I can be confident that the foundations of my experience, and on which my code depends, isn’t going to be ripped out from under me with the next update in six months.
Web and Mobile
In my mind, Java really sailed down the wrong channel when the winds of mobile and web came howling in. JavaFX’s lack of support for the two most important platforms of its day made it a non-starter. Perhaps, if it had come out of the gate with web and mobile support, history would have unfolded differently, but that is just conjecture. There were forces outside of their control that may have made any other outcome impossible or unlikely.
But I have good news! JavaFX isn’t the only story for Java UI in the past decade. The Java community is large and diverse, and there were others ready take up the cause.
Codename One, launched in 2012, offered a Swing-like, cross-platform UI toolkit for building iOS and Android apps in Java. (Full disclosure: I work for Codename One). They have since expanded their offering to support all major desktop platforms and also the web, using TeaVM.
Swing apps can now be deployed to the web using CheerpJ, if you’re willing to wait for it to download the full JRE as compiled Javascript. There are also solutions, now, for deploying JavaFX to the web3 and mobile4.
SnapKit
One last project I’d like to highlight in this space is SnapKit. SnapKit is a light-weight Java UI kit that does what Swing and JavaFX both failed to do: run well in the browser. It includes a fairly comprehensive set of components and a slick and snappy GUI builder. SnapKit apps can be deployed to the web or the desktop. Web deployment uses TeaVM under the covers to compile to JavaScript. Desktop deployments can use either Swing or JavaFX as a foundation.
You probably haven’t heard of SnapKit because the author hasn’t promoted it at all. He has honed it for years as a UI toolkit that powers ReportMill and other apps that he develops. Because he has incubated it for so long internally, he has been able to keep the API very clean and opinionated, resulting in remarkably easy-to-maintain codebases.
You can download the desktop version of SnapBuilder, the GUI builder for SnapKit, here.
For a really good demonstration of the toolkit, try out SnapCharts, which can be downloaded here.
Jeff, the author, has also posted a handful of demos here, if you want to see it running in the browser.
Recently, he’s added 3D support, which uses WebGL under the hood in browser deployments, and JOGL in desktop deployments. Personally, I’m very excited to see where he takes this.
SnapKit hasn’t been “officially” released, so you should contact Jeff, if you’re interested in using it. I’m sure he’ll be happy to hear from you.
Can’t Cover Everything
The topic of cross-platform UI kits is too extensive to cover comprehensively in a blog post, so I’m necessarily leaving lots out (e.g. QT has Java bindings, and I didn’t mention SWT). I just wanted to give an overview of the landscape as I see it, partly so I can refer back to this article later if jDeploy users are curious about their options for UI toolkits on the desktop.
The overarching goal of this newsletter is to help to unify the Java desktop (client-side) community, and make Java a more compelling platform for desktop development. I write both historical articles to help provide context as to why I built jDeploy, and also to share the narratives of my time, as I see them. I will also be writing some more technical articles on the making of jDeploy as well as tutorials on how to use it to deploy your Java apps as native bundles on Mac, Windows, and Linux.
If you’re interested in receiving weekly articles of this nature, then don’t forget to subscribe to the newsletter.
Before Java was called Java, it was called OAK for a while.
Gluon, the company that now maintains JavaFX, also provides iOS and Android ports now.
The single biggest benefit of Swing and the single biggest drawback of JavaFX was native look and feel. Swing had it, and the implementation was great. JavaFX doesn't have it. There are a lot of haters that say even swing native L&F looks off, one *could* take care to get a great native experience but that was impossible with JavaFX.