Practical JavaFX 2, Part 1: Architecture of a Swing-based notepad

How will Swing JPad's UI features map to JavaFX 2.0?

With the release of JavaFX 2.0 the question for many Java developers is whether it's time to start transitioning Swing applications to this new UI paradigm, and what better way to find out than by a hands-on exercise? In this three-part tutorial, Jeff Friesen walks through the process of refactoring an example Swing-based notepad editor application to its JavaFX 2 equivalent.

JavaFX 2 is a user interface library and platform for creating and running modern Java application UIs that feature rich graphics, animation, media, controls, and more. It's also a toolkit for deploying JavaFX applications, meaning any Java application that features a JavaFX UI. A JavaFX application can be deployed standalone, launched via Java Web Start, or embedded in a web page.

This article serves as a practical introduction to JavaFX 2, and is geared to developers experienced with Swing-based user interface (UI) development. By refactoring a Swing application, you'll familiarize yourself with some of the most useful features of JavaFX 2. You'll have the opportunity to try it on for size. And you'll get some practice in refactoring Swing apps to JavaFX -- which could come in handy if JavaFX 2 turns out to be the next-generation Java development platform that Oracle clearly hopes it will.

Oracle has been pushing JavaFX 2 as the next-generation platform for Java, but does that mean it's time to jump from Swing to JavaFX? Refactoring a Swing demo app to JavaFX is one way to find out.

This first part of the article is an overview of a Swing demo application, a text editor called JPad. Experienced Swing developers can probably give the architecture a glance, then move on to refactoring the app for JavaFX in Part 2 and Part 3. For those less experienced with Swing, Part 1 offers valuable insight into client-side application development, which you'll probably need in order to follow the refactoring examples.

JavaFX 2 release history

Oracle released JavaFX 2.0 beta in May 2011 and declared it production-ready in October of that same year. The most recent version, JavaFX 2.0.2, was released in December 2011 with some bug fixes and minor features added. Examples in this article are based on JavaFX 2.0.2.

Let's start with a review of new features in JavaFX 2, some of which you'll encounter in the refactored JPad architecture.

Big changes in JavaFX 2

JavaFX 2 differs significantly from previous versions, and one of the biggest changes is the deprecation of JavaFX Script. JavaFX developers are free to program in Java code, and may also experiment with JVM scripting languages such as Groovy or Scala. Java code is more verbose than JavaFX Script, but there's hope that Java 8's support for lambda expressions in the Java language will bring improvements.

The following JavaFX Script language features have been replaced with Java API equivalents:

  • Java's builders replace JavaFX Script's script initialization blocks. A builder is a Java class whose methods are designed so that you can chain together method calls.
  • Change listeners replace replace triggers.
  • Observable lists replace sequences.
  • Single abstract method (SAM) types replace function types. A SAM type is a single-method interface or abstract class -- the abstract class's method is abstract. When Java 8 arrives, we'll be able to use lambda expressions to more concisely work with SAM types.
  • Properties based on an expanded and improved version of the JavaBeans model have been introduced to support binding. Whereas JavaFX Script enabled binding to any variable, JavaFX 2.0 only allows binding to properties.

Further changes to JavaFX 2 include the following:

  • Experimental menu, toolbar, and tree UI controls introduced in JavaFX 1.3.1 are now standard. JavaFX 2 also features new table and HTML-editor controls along with standard file dialogs. All controls can be skinned via CSS3+.
  • A new web-view component based on WebKit makes it possible to embed a web browser (with JavaScript and Document Object Model support) into Java applications.
  • JavaFX 2 supports FXML, a scriptable, XML-based markup language for constructing UIs. FXML replaces the former JavaFX Script-oriented FXD (JavaFX Data) format.
  • JavaFX application deployment has been improved. You can now create custom preloaders (specialized JavaFX applications extending the javafx.application.Preloader class) to enhance application loading and startup.
  • JavaFX 2's application architecture has been improved to simplify the organization of applications that will run in multiple deployment contexts. New APIs enable applications to detect and interact with their deployment environments.
  • A new platform architecture based on a hardware-accelerated graphics pipeline (Prism) and windowing toolkit (Glass) improves the performance of JavaFX 2 applications. When combined with Java ME, it should help in migrating JavaFX applications to mobile and tablet devices.

Is JavaFX 2 production ready?

As you read this article, consider the following questions:

  • Is JavaFX 2 a production-ready platform, as Oracle claims?
  • Are there notable advantages to programming in JavaFX versus Swing?

I'll share my answers to these questions at the end of Part 3. You'll also be invited to share your viewpoint in the article's discussion forum.

Introducing JPad

JPad is a Java-based text editor application that's very similar to Windows XP's Notepad program. As with Notepad, JPad lets you open a document file, make changes to its text, and save your changes to the currently open file or a new document file. You can also undo changes, interact with the clipboard through cut/copy/paste operations, and perform other tasks. Figure 1 is a screenshot of JPad's UI.

Figure 1. A screenshot of Swing JPad (click to enlarge)

Figure 1 reveals a simple UI that consists of a menu bar, a scrollable text area, and a status bar. The menu bar displays File, Edit, Format, and Help menus; the text area displays the current document and lets you make changes; and the status bar displays a default message or the help text that's associated with the currently selected menu. Note that the Cut and Copy functions are disabled until text is selected.

File offers the following menu items:

  • New creates a new empty document and prompts users to save the current document if it has unsaved changes.
  • Open opens an existing file. Users are prompted to save the current document if there are unsaved changes.
  • Save saves the current document to its associated file or a different file.
  • Save As saves the current document to a different file.
  • Exit exits the application. Users are prompted to save the current document if there are unsaved changes.

Edit offers the following menu items:

  • Undo undoes the last edit; disabled when there are no new edits.
  • Cut cuts the selected text to the clipboard; disabled when no text is selected.
  • Copy copies the selected text to the clipboard; disabled when no text is selected.
  • Paste pastes clipboard text over selected text or at the caret position; disabled when the clipboard has no text.
  • Select All selects all of the text area's text.

Additionally:

  • Word Wrap can be enabled or disabled by checking or unchecking the Format menu item.
  • About JPad is a menu item under Help that lets users obtain information about the application.

The JPad application also lets users specify a file to be opened as a command-line argument. If a user drops a file being dragged onto its text area, JPad will open that file after letting the user save any recent changes.

Setting up a JavaFX 2 development environment

Oracle's JavaFX website extensively documents the JavaFX architecture. That is also where you'll find JavaFX development software for your 32-bit or 64-bit Windows or Mac OS X platform. I used the JDK 7u2 with JavaFX SDK integrated software package (which includes the JavaFX 2.0.2 SDK) on a Windows XP platform to develop my JavaFX 2 JPad application.

Compiling and running JPad

In order to explore JPad in your development environment, download its source code and resource file: JPad.java and icon.png. After extracting these files to your current directory, execute the following commands to compile JPad.java and run it. Note that the third command tells JPad to open and display the contents of JPad.java:

javac JPad.java
java JPad
java JPad JPad.java

Architecture of JPad

JPad is implemented as a single JPad top-level class. As with many Swing applications, JPad extends the javax.swing.JFrame class, which makes it a kind of frame window. You can see this in Listing 1.

Listing 1. The JPad class encapsulates the JPad application

// ...
public class JPad extends JFrame
{
   // ...
   public JPad(String[] args)
   {
      // ...
      if (args.length != 0)
         doOpen(new File(args[0]));
   }
   private void doExit()
   {
      // ...
   }
   private void doNew()
   {
      // ...
   }
   private void doOpen()
   {
      doOpen(null);
   }
   private void doOpen(File file)
   {
      // ...
   }
   private boolean doSave()
   {
      // ...
   }
   private boolean doSaveAs()
   {
      // ...
   }
   private String read(File f) throws IOException
   {
      // ...
   }
   private void write(File f, String text) throws IOException
   {
      // ...
   }
   public static void main(final String[] args)
   {
      Runnable r = new Runnable()
      {
         @Override
         public void run()
         {
            new JPad(args);
         }
      };
      EventQueue.invokeLater(r);
   }
}

Listing 1 shows you JPad's skeletal framework, which consists of a constructor, several private methods, and the main() entry-point method. (I've commented out everything else because it isn't relevant at this point.)

The main() method instantiates JPad and passes to its constructor the array of command-line arguments on the event-dispatch thread. After creating JPad's UI, the constructor opens the file identified by the first command-line argument when at least one argument is specified.

Swing UIs and the event-dispatch thread

In the JavaWorld article "Swing threading and the event-dispatch thread" author John Zukowski explains why it's important to create Swing UIs on the event-dispatch thread.

Creating the UI

JPad's constructor creates the JPad UI by leveraging the javax.swing package's JCheckBoxMenuItem, JLabel, JMenu, JMenuBar, JMenuItem, JScrollPane, and JTextArea classes, as shown in Listing 2.

Listing 2. Swing organizes JPad's UI into a menu bar and a content pane

// ...
private JTextArea ta;
// ...
private JLabel lbl;
// ...
public JPad(String[] args)
{
   // ...
   JMenuBar mb = new JMenuBar();
   JMenu mFile = new JMenu("File");
   JMenuItem miNew = new JMenuItem("New");
   miNew.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
                                               KeyEvent.CTRL_MASK));
   // ...
   mFile.add(miNew);
   // ...
   mb.add(mFile);
   // ...
   JMenu mFormat = new JMenu("Format");
   final JCheckBoxMenuItem cbmiWordWrap = new JCheckBoxMenuItem("Word Wrap");
   // ...
   mFormat.add(cbmiWordWrap);
   // ...
   mb.add(mFormat);
   // ...
   setJMenuBar(mb);
   getContentPane().add(new JScrollPane (ta = new JTextArea()));
   // ...
   getContentPane().add(lbl = new JLabel("JPad 1.0"), BorderLayout.SOUTH);
   setSize(400, 400);
   setTitle(DEFAULT_TITLE);
   // ...
   setVisible(true);
   // ...
}

The constructor first initializes the JFrame superclass and creates/installs the menu system (although I've omitted most of this code for brevity). Next, it creates the scrollable text area and status bar, and adds them to the frame window's content pane. Lastly, it initializes and displays the window.

Why use setTitle(DEFAULT_TITLE);?

Experienced Swing developers might note that I've used setTitle(DEFAULT_TITLE); instead of super(DEFAULT_TITLE); to initialize the frame window's default titlebar text in Listing 2. I wrote the code this way because the JavaFX 2 application will also require setTitle(). For the sake of example, I wanted JPad to be as consistent with JPadFX as possible.

Handling events

Event handling is one of the more involved aspects of Swing development, which makes refactoring a Swing app's event-handling infrastructure to JavaFX both challenging and rewarding. Here, we'll look at how JPad handles typical notepad events triggered by the user, namely: action, document, menu, caret, and window events.

Action events

An action event is fired when the user selects a menu item. For example, when the user selects New from the File menu, an action event is sent to New's registered action listener, as shown in Listing 3.

Listing 3. JPad responds to a New action event

ActionListener al;
al = new ActionListener()
{
   @Override
   public void actionPerformed(ActionEvent ae)
   {
      if (fDirty)
         switch (JOptionPane.showConfirmDialog(JPad.this, SAVE_CHNGS,
                                               TITLE_AYS,
                                               JOptionPane.YES_NO_OPTION))
         {
            case JOptionPane.YES_OPTION: if (doSave()) doNew(); break;
            case JOptionPane.NO_OPTION : doNew();
         }
      else
         doNew();
   }
};
miNew.addActionListener(al);
1 2 3 Page 1
Page 1 of 3