Dustin Marx is a software developer who enjoys identifying and using the correct tool for the job. In addition to writing software and writing a blog on software development, Dustin occassionally presents at conferences and writes articles. Dustin is a DZone MVB and is not an employee of DZone and has posted 211 posts at DZone. You can read more from them at their website. View Full User Profile

Focus on JavaFX 2 FXML with NetBeans IDE 7.1

01.26.2012
| 11002 views |
  • submit to reddit

In October 2011, I used the post Hello JavaFX 2.0: Introduction by NetBeans IDE 7.1 beta to look at using NetBeans IDE 7.1 beta to build a simple Hello, World style of JavaFX 2.0 application. In this post, I look at using NetBeans IDE 7.1 (no longer in beta) to build a slightly more sophisticated JavaFX 2 application that makes heavy use of FXML.

Most of my posts on JavaFX 2 up to this point have emphasized the "pure Java" nature of JavaFX 2. The examples in these posts have been written in standard Java using JavaFX 2 APIs directly from the Java source code. These examples have also been built with the javac compiler and executed with the java application launcher. In this post, I take one of those examples [(Pure Java) JavaFX 2.0 Menus] and "port" it to use FXML for presentation layout.

For convenience, I include the entire Java listing for the "pure Java" implementation of the JavaFX 2 menus demonstration.

JavaFX 2.0 Menus Demonstration (Pure Java)
package dustin.examples;

import static java.lang.System.out;

import javafx.application.Application;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 * Example of creating menus in JavaFX.
 * 
 * @author Dustin
 */
public class JavaFxMenus extends Application
{
   /**
    * Build menu bar with included menus for this demonstration.
    * 
    * @param menuWidthProperty Width to be bound to menu bar width.
    * @return Menu Bar with menus included.
    */
   private MenuBar buildMenuBarWithMenus(final ReadOnlyDoubleProperty menuWidthProperty)
   {
      final MenuBar menuBar = new MenuBar();

      // Prepare left-most 'File' drop-down menu
      final Menu fileMenu = new Menu("File");
      fileMenu.getItems().add(new MenuItem("New"));
      fileMenu.getItems().add(new MenuItem("Open"));
      fileMenu.getItems().add(new MenuItem("Save"));
      fileMenu.getItems().add(new MenuItem("Save As"));
      fileMenu.getItems().add(new SeparatorMenuItem());
      fileMenu.getItems().add(new MenuItem("Exit"));
      menuBar.getMenus().add(fileMenu);

      // Prepare 'Examples' drop-down menu
      final Menu examplesMenu = new Menu("JavaFX 2.0 Examples");
      examplesMenu.getItems().add(new MenuItem("Text Example"));
      examplesMenu.getItems().add(new MenuItem("Objects Example"));
      examplesMenu.getItems().add(new MenuItem("Animation Example"));
      menuBar.getMenus().add(examplesMenu);

      // Prepare 'Help' drop-down menu
      final Menu helpMenu = new Menu("Help");
      final MenuItem searchMenuItem = new MenuItem("Search");
      searchMenuItem.setDisable(true);
      helpMenu.getItems().add(searchMenuItem);
      final MenuItem onlineManualMenuItem = new MenuItem("Online Manual");
      onlineManualMenuItem.setVisible(false);
      helpMenu.getItems().add(onlineManualMenuItem);
      helpMenu.getItems().add(new SeparatorMenuItem());
      final MenuItem aboutMenuItem =
         MenuItemBuilder.create()
                        .text("About")
                        .onAction(
                            new EventHandler<ActionEvent>()
                            {
                               @Override public void handle(ActionEvent e)
                               {
                                  out.println("You clicked on About!");
                               }
                            })
                        .accelerator(
                            new KeyCodeCombination(
                               KeyCode.A, KeyCombination.CONTROL_DOWN))
                        .build();             
      helpMenu.getItems().add(aboutMenuItem);
      menuBar.getMenus().add(helpMenu);

      // bind width of menu bar to width of associated stage
      menuBar.prefWidthProperty().bind(menuWidthProperty);

      return menuBar;
   }

   /**
    * Start of JavaFX application demonstrating menu support.
    * 
    * @param stage Primary stage.
    */
   @Override
   public void start(final Stage stage)
   {
      stage.setTitle("Creating Menus with JavaFX 2.0");
      final Group rootGroup = new Group();
      final Scene scene = new Scene(rootGroup, 800, 400, Color.WHEAT);
      final MenuBar menuBar = buildMenuBarWithMenus(stage.widthProperty());
      rootGroup.getChildren().add(menuBar);
      stage.setScene(scene);
      stage.show();
   }

   /**
    * Main executable function for running examples.
    * 
    * @param arguments Command-line arguments: none expected.
    */
   public static void main(final String[] arguments)
   {
      Application.launch(arguments);
   }
}

Besides adding the ability to write JavaFX applications in "pure Java" as implemented in the last code listing, JavaFX 2 also provides FXML, an XML grammar for specifying a JavaFX application's layout. This approach is very similar to that provided by Flex's MXML, by OpenLaszlo's LZX, and by Mozilla's XUL. This approach works well with JavaFX's hierarchical Scene Graph.

The ported FXML code is shown next. It is easy to see the presentation structure in this XML.

JavaFx2Menus.fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox id="vbox" prefHeight="400" prefWidth="800"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="dustin.examples.MenuController">
   <MenuBar fx:id="menuBar" onKeyPressed="#handleKeyInput">
      <menus>
         <Menu text="File">
            <items>
               <MenuItem text="New"/>
               <MenuItem text="Open"/>
               <MenuItem text="Save"/>
               <MenuItem text="Save As"/>
               <SeparatorMenuItem  />
               <MenuItem text="Exit"/>
            </items>
         </Menu>
         <Menu text="JavaFX 2.0 Examples">
            <items>
               <MenuItem text="Text Example"/>
               <MenuItem text="Objects Example"/>
               <MenuItem text="Animation Example"/>
            </items>
         </Menu>
         <Menu text="Help">
            <items>
               <MenuItem text="Search" disable="true"/>
               <MenuItem text="Online Manual" visible="false"/>
               <SeparatorMenuItem />
               <MenuItem text="About" onAction="#handleAboutAction"/>
            </items>
         </Menu>
      </menus>
   </MenuBar>
</VBox>

The important portion of this simple FXML can be viewed on a single page within NetBeans IDE 7.1 as shown in the next screen snapshot.

Besides the conciseness of the FXML and the aesthetically pleasing hierarchical presentation, the FXML also should look familiar to anyone who has worked with Flex, OpenLaszlo, or XUL. Most of the presentation structure is easily identified in the FXML document. This example includes an onAction handler that responds to the "About" menu item and there is an onKeyPressed action associated with the menu bar as well (I could not figure out a way to easily associate this handler with the menu item itself). Both of these actions reference names that start with the # symbol. The names following the # are names of methods on the controller class (defined by the fx:controller attribute of the VBox as the class named dustin.examples.MenuController). The source for this controller class is shown next.

MenuController.java
package dustin.examples;

import static java.lang.System.out;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.MenuBar;
import javafx.scene.input.InputEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

/**
 * Controller class for JavaFX 2 Menus with FXML post and demonstration.
 * 
 * @author Dustin
 */
public class MenuController implements Initializable
{
   @FXML
   private MenuBar menuBar;

   /**
    * Handle action related to "About" menu item.
    * 
    * @param event Event on "About" menu item.
    */
   @FXML
   private void handleAboutAction(final ActionEvent event)
   {
      provideAboutFunctionality();
   }

   /**
    * Handle action related to input (in this case specifically only responds to
    * keyboard event CTRL-A).
    * 
    * @param event Input event.
    */
   @FXML
   private void handleKeyInput(final InputEvent event)
   {
      if (event instanceof KeyEvent)
      {
         final KeyEvent keyEvent = (KeyEvent) event;
         if (keyEvent.isControlDown() && keyEvent.getCode() == KeyCode.A)
         {
            provideAboutFunctionality();
         }
      }
   }

   /**
    * Perform functionality associated with "About" menu selection or CTRL-A.
    */
   private void provideAboutFunctionality()
   {
      out.println("You clicked on About!");      
   }

   @Override
   public void initialize(final URL url, final ResourceBundle rb)
   {
      menuBar.setFocusTraversable(true);
   }   
}

The above code from the controller class includes the two methods referenced as action handlers in the FXML with the # prefixes. These two methods are private and normally would not be visible to the FXML loader, but the use of the annotation @FXML gets around this problem. The menuBar attribute is also private but visible to the FXML loader thanks to its own @FXML annotation.

To get the application to respond to CTRL-A before any initial access of the menu bar, I found that I needed to have a reference to the menu bar in the controller and call setFocusTraversable(true) on that instance. Without doing that, the CTRL-A would only be responded to if I had first clicked on the menu bar once. The hook from the FXML to this attribute in the controller is the fx:id attribute used in the FXML. That attribute references the name "menuBar", which is (not coincidentally) also the name of the attribute in the controller class. In other words, the attribute fx:id="menuBar" ties the XML element that attribute is on in the FXML to the attribute with the same name in the controller.

I did not show much in this post specific to NetBeans IDE 7.1, but it is worth noting that I had all the pertinent files created in a new project using NetBeans IDE 7.1's New Project wizard and selecting a JavaFX FXML Application as the project type. This created a basic FXML file, a basic controller file, and a main class for loading the FXML and running the application. I modified these to the examples above for the FXML and for the controller class. The modified class that loads the FXML and starts the JavaFX application is shown next. (There's not a lot to it!)

JavaFxMenusWithFxmlDemo.java
package dustin.examples;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 * Simple example of JavaFX 2 FXML used to demonstrate JavaFX 2 menus. This is a
 * "port" of the example featured using "pure Java" (no FXML) in the post
 * "(Pure Java) JavaFX 2.0 Menus"
 * (http://marxsoftware.blogspot.com/2011/12/pure-java-javafx-20-menus.html).
 * 
 * @author Dustin
 */
public class JavaFxMenusWithFxmlDemo extends Application
{
   /**
    * Main function for running this application.
    * 
    * @param arguments Command-line arguments: none expected.
    */
   public static void main(String[] arguments)
   {
      Application.launch(JavaFxMenusWithFxmlDemo.class, arguments);
   }

   /**
    * Overridden Application.start(Stage) method.
    * 
    * @param stage Primary stage.
    * @throws Exception JavaFX application exception.
    */
   @Override
   public void start(final Stage stage) throws Exception
   {
      final Parent fxmlRoot = FXMLLoader.load(getClass().getResource("JavaFx2Menus.fxml"));
      stage.setScene(new Scene(fxmlRoot));
      stage.show();
   }
}

I won't show the output here because it's essentially what the "pure Java" version produced, which is shown in my post on that approach. Besides generating the files that I adapted for my application, NetBeans IDE 7.1's new project wizard for a JavaFX FXML application also created a project that automatically packages up what I need for my JavaFX application in an executable JAR. The contents of the JAR in this case are shown in the next screen snapshot.

The trickiest part about using FXML is the relatively less available documentation. The pure Java APIs have the strong Javadoc API documentation, but I know of no such thing for FXML (including no documentation or schema for the namespace http://javafx.com/fxml). There are some great resources of FXML such as Getting Started with FXML and Introduction to FXML. Perhaps the best document for figuring out what elements and attributes FXML provides is Introducing FXML: A Markup Language for JavaFX, which explains the "mapping" of JavaFX standard Java API classes to JavaFX FXML elements and attributes.

FXML appears to be a fairly attractive approach for building a JavaFX application because it nicely separates presentation (FXML) from business logic (controller class and classes that would be called by controller methods). This can make it easier to visually see the GUI layout in the hierarchical XML and keep the logic and presentation from cluttering each other.

From http://marxsoftware.blogspot.com/2012/01/focus-on-javafx-2-fxml-with-netbeans-71.html

Published at DZone with permission of Dustin Marx, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Mihai Dinca - P... replied on Thu, 2012/01/26 - 4:00am

I needed to put <children> tag in JavaFx2Menus.fxml

before MenuBar tag.

otherwise an Exception is raised:

javafx.fxml.LoadException: javafx.scene.layout.VBox does not have a default property.

Dustin Marx replied on Fri, 2012/01/27 - 1:58am

I'm glad you pointed this out because I neglected to state in the post that my example was using JavaFX 2.1 (beta). My guess is that you're using JavaFX 2.0 and, if so, that would explain the difference. JavaFX 2.1 applies the @DefaultProperty annotation to VBox's parent (Pane) to specify that when no sub-element is specified, the default is "children."

Thanks for bringing this up so that anyone reading this post will be aware of the differences between JavaFX 2.0 and JavaFX 2.1. Your addition of explicit specification of the "children" element should work in either version.

UPDATE: Here is a link to my post with greater details.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.