Leveraging Java for Great User Experiences and the Internet of Things

JavaFX Applets

August 17, 2009

As I mentioned the previous article in this series, the next step in the development of the BandmatesFX program will be to enable the user to navigate more general information than just musicians and bands. In this article, I've added the ability to navigate soccer players and their teams. In addition, the program (whose working title is now Topic Map Navigator), can be deployed as a full-browser-page JavaFX applet. Shown below is a screenshot of the program running in a Chrome 2.0 browser, and it also runs in FireFox 3.5+ and Internet Explorer 7+. You'll usually need have Java SE 6 update 10+ in order for the applet to run in the browser (e.g. on Mac with Java 1.5 this app opens in its own window).

One of the features of this program is that it opens a web page when you right-click on an image. Deploying this program in the browser makes this a tighter experience, because the page appears on top of the Topic Map Navigator program. When you subsequently close the web page, the program is revealed again. The screenshot below shows the result of clicking the secondary mouse button on the picture of Lionel Messi, and then clicking the Wikipedia link from his Freebase web page that appears. You may also recall from previous articles in this series that clicking the primary mouse button on an image moves that person to the top of the app.

If you have a late-model Chrome, FireFox or Internet Explorer browser, and Java SE 6 update 10+, go ahead an run this program as a JavaFX applet. If not, you can launch the program via Java Web Start by clicking the Launch button below. Note: Please leave a comment if you have any difficulty deploying this JavaFX applet, providing your hardware/OS/browser/Java version information. I'll report these difficulties to the JavaFX JIRA issue tracker, as I'd like for the JavaFX applet deployment experience to be as solid as it is when deploying with Java Web Start.

Exercising Resizing

In order for the JavaFX applet to occupy the full browser page, we change the width and height of the applet to 100% in the HTML file as shown in the snippet below.

When the browser page is resized, the width and height values of the JavaFX Stage change dynamically. You can bind to these values and control the appearance of the program, using layout classes that are provided with JavaFX. In addition, the JFXtras project has some classes such as ResizableScene, ResizableCustomNode, ResizableHBox and ResizableVBox that make quick work of responding to a resized Stage. For example, the Topic Map Navigator program uses these classes, and the resizable Shelf class, to automatically reposition and scale the cover flows when the user resizes the program.

Detect whether the JavaFX program is running as an applet in the browser.

Open a web page in a browser, whether running as an applet or from Java Web Start.

Use the GoogleMap custom node from Sergey Surikov's open source CRUDfx project (the treasure trove mentioned in the title) to display an interactive Google map in a JavaFX program.

Before we get into how to do these things, take the newest iteration of the BandmatesFX app for a spin, making sure that you test out the new functionality mentioned above.

There are some pointers after the screenshot below to help you along.

After invoking the BandmatesFX app, start typing (for example) Axl Rose and notice that a list appears suggesting William Bailey (Axl's given name).

Click on William in the list, and the screen should look almost like the screenshot above. Note: After this was posted, I altered the queries so that if an image of an artist or band isn't available in Freebase (and therefore not in Wikipedia), it will show a placeholder image.

Hover the mouse on the images in each of the cover flows, noticing that the name of the band or artist appears in a popup window on the upper left side of the images.

Click the secondary mouse button on any of the images, and the Freebase.com page for that band or artist should appear in the browser.

Look at the map in the upper right portion of the application, noticing that Axl was born in Lafayette, Indiana (which is about 80 miles from where I live). Try clicking on the map and dragging the mouse, which should expose other parts of Indiana and the United States.

Recall from the last article in this series that clicking the artist that is in the center of the bottom Shelf will move his/her picture to the top of the app. The bands and artists in the cover flows will be replaced accordingly. Trivia: According to the screenshot above, the given name of the Guns N' Roses lead guitarist is Saul Hudson (who is more widely known as Slash).

Some Example Code Snippets

Here are some code snippets behind the new functionality:

/** * Use the availability of the eval function (that uses the browser's * JavaScript engine), as an indicator that we're running as an applet */def runningAsApplet:Boolean = AppletStageExtension.eval("2+3") != null;

March 02, 2009

As many of you know, Weiqi Gao, Stephen Chin, Dean Iverson and I have been writing a book entitled
Pro JavaFX that is targeted at serious developers who want to use JavaFX. In addition to the book being available in printed form by JavaOne 2009, we made the decision last week to allow the book to be offered as an Apress Alpha (early access) eBook. This enables you to see the chapters of the book as drafts are submitted and revised, which gives you access to the content as soon as it is created (typos and all). This also enables us to fine-tune the material based upon your feedback.

Here's the list of the chapters for the book:

Getting a Jump Start in JavaFX

Taking a Closer look at the JavaFX Language

Creating a User Interface in JavaFX

Using Functions, Classes and Other Advanced Features

Creating Custom UI Components in JavaFX

Using the Media Classes

Dynamically Laying out Nodes in the User Interface

Extending JavaFX with Third-Party Libraries

Building a Professional JavaFX Application

Developing JavaFX Mobile Applications

Currently, seven chapters (1, 2, 3, 4, 6, 7, 8) are available for download, and each of these is in first or second draft. Chapters 5, 9 and 10 will be available soon as first drafts. Here is a screenshot from Chapter 8 in which a drawing program built in JavaFX (in Chapter 5) is used to draw the shapes available in the JFXtras project:

Weiqi, Stephen, Dean and I welcome you to join us in this journey as we complete the development of what is becoming a definitive treatment of JavaFX for professional developers.

Note: Thanks to reader "mbien" (see comments) for pointing our that the colors of the original applet in this post were hideous (my words). I then consulted graphics designer Mark Dingman of Malden Labs who gave me a graphical mock-up from which I created the above applet. Here's the code for this applet, updated for the JavaFX SDK preview:

According to Sun's Java Deployment Toolkit overview page, "Desktop clients have a wide variety of Java Platforms installed, from the Microsft VM to Sun's latest Java SE 6 updates. They run various operating systems from Sun, Microsoft, Apple, Red Hat, and others, and are connected to the internet at a wide range of connection speeds. How are content providers to deliver Java content to all of these clients with the best possible user experience?

Various sources have published JavaScript techniques for detecting and deploying the Java Platform for use by Java Plug-In applets and Java Web Start applications. These scripts generally have serious limitations and fail to support the varied combinations of browser, OS, and configuration options found on today's clients.

The Java Deployment Toolkit allows developers to easily deploy applets and applications to a large variety of clients with JavaScripts. It also provides advice on using some of the most powerful features available in Java Web Start and Java Plug-In, and an outline of the differences between these two deployment vehicles."

In a nutshell, the Java Deployment Toolkit is a JavaScript library maintained by Sun and always available at runtime by your HTML code. This library has several methods that perform tasks such as sensing Java-related infrastructure and installing the JRE on client machines. We'll use one of these methods, namely runApplet, to run a JavaFX applet with a specified minimum JRE version. Here's the HTML and JavaScript code I'm using to deploy today's example applet:

Notice that the above code enables dragging the applet onto the desktop, as well as using Pack200 formatted JAR files, if the client machine has Java SE 6 update 10 installed. Give the applet a whirl to see its deployment behavior on your machine. By the way, according to the Java SE 6 Update 10 plug-in docs, "by default, the gesture to drag the applet out of the web browser is Alt + Left click + Drag."

September 04, 2008

In the JFX Custom Nodes category of this blog, graphics designer Mark Dingman of Malden Labs and I have been
collaborating on an imaginary Sound Beans application. This category contains a growing series of posts in which we are demonstrating how to
create JavaFX UI custom controls. This series also provide a case study in how a graphics
designer and an application developer can work together effectively in
developing JavaFX applications. Today I'd like to highlight the recent Google Chrome browser announcement by showing you how to create and run a JavaFX applet in Chrome. Here's a screenshot of the TableNode example from an earlier post running as a JavaFX applet in Chrome:

To try this out, first obtain Google Chrome and install it. Then obtain Java SE 6 Update 10 and install it as well. By the way, installing Java SE 6 update 10 will enable this JavaFX applet to run on Firefox 3 and Internet Explorer as well. Go ahead and run this example, being sure to scroll the custom TableNode control and to click on its rows. Also, select the Burn icon and move the slider to demonstrate the custom ProgressNode control.

Looking at the Code

In addition to the ButtonNode.fx, MenuNode.fx, DeckNode.fx, ProgressNode.fx and TableNode.fx files from previous posts in this series, you'll need the following files:

TableNodeExampleApplet.fx:

/* * TableNodeExampleApplet.fx - * An example of using the TableNode custom node in an Applet. It also * demonstrates the ProgressNode, DeckNode, MenuNode and ButtonNode * custom nodes * * Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com) * to demonstrate how to create custom nodes and applets in JavaFX */package com.javafxpert.table_node_example.ui;

Note that the Application class has a stage attribute just as the Frame had in previous examples. Here's the TableNodeExamplePage.html file that you'll open in your browser. The draggable param, by the way, enables that neat "pull the applet out of the browser" trick that I'll show you in a bit:

As shown in the following screenshot, one of the cool features of Java SE 6 update 10 is that you can drag a Java or JavaFX applet out of the browser and onto the desktop. By default, you press the Alt key while dragging the applet:

Here is our JavaFX Applet living happily on the desktop after the browser has been closed, and the user has selected the Burn page:

Google Chrome will be a Driving Force for RIA

According to Google, Java SE 6 Update 10 is the version that must be used in order to run Java in the Chrome browser. As I've mentioned previously, one of the objectives of Java SE 6 Update 10 is to solve the JRE and Java/JavaFX deployment issues. Because Google Chrome is destined to be a great, cross-platform browser, and because it requires the version of Java that makes rich-client Java/JavaFX programs feasible, this will increase the adoption rate of JavaFX applets and applications.

By the way, Bruno Ghisi (a fellow Java Champion) and Lucas Torri have developed a Java Bluetooth Framework called Marge. Using that framework they developed a mobile phone game controller for this TetrisJFX program. You can see a video of this in action in one of Bruno's blog posts.

As you can see, I've added some functionality since the previous post, but here are some highlights:

A preview area in the upper right corner that shows the next tetromino shape that will drop.

Scoring is more Tetris-like, in that each tetromino is worth 25 points when it is finished dropping, and each row that fills up is worth 100 points. Full rows are removed from the playing field and the tetrominoes above are moved down.

The button on the left toggles between Play and Stop. If you choose to Stop the game, "Game Over" will appear in the background, followed by which pressing Play will start a new game.

When the tetrominoes stack up to the top, as shown in the sceenshot above, "Game Over" appears in the background and the game stops.

Here is the code for this version of TetrisJFX. Note that I've added a new class named TetrisNextShapeNode that shows the next tetromino shape to drop.

TetrisMain.fx:

/* * TetrisMain.fx - The main program for a compiled JavaFX Script Tetris game * * Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com) * to serve as a compiled JavaFX Script example. */package tetris_ui;

/* * TetrisPlayingField.fx - * A custom graphical component that is the UI for the * playing field. * * Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com) * to serve as a compiled JavaFX Script example. * */package tetris_ui;

/** * This value is incremented via the KeyFrame animation mechanism, * and represents the row in which the pivotal block is currently residing. */ public attribute a:Integer on replace oldVal { if (a == pieceAppearRow) { activeShapeType = nextShapeType;

// Remove the current "next shape type" from the preview area updateNextShapePreviewCells(true);

nextShapeType = TetrisShapeType.randomShapeType(); // Load the new "next shape type" into the preview area updateNextShapePreviewCells(false);

public function rotate90():Void { if (stopDropping) {return;} // Don't try to process if a piece isn't falling updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, true); for (oldCell in TetrisShapeType.squarePositionsForRotatedShape(activeShapeType, computeNewAngle(tetrominoAngle, 90))) { // First check to see if the rotated tetromino is past the // left side of the playing field if (tetrominoHorzPos + oldCell.x < 0) { updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); return; } // Then check to see if the tetromino is at the right of // the playing field if (tetrominoHorzPos + oldCell.x > NUM_COLS - 1) { updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); return; } // Now check to see if another tetromino is preventing it from rotating if (fieldCells[((a + oldCell.y) * NUM_COLS + tetrominoHorzPos - 1 + oldCell.x) as Integer] <> TetrisShapeType.NONE) { updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); return; } } tetrominoAngle = computeNewAngle(tetrominoAngle, 90); updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); }

public function moveLeft():Void { if (stopDropping) {return;} // Don't try to process if a piece isn't falling updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, true); for (oldCell in TetrisShapeType.squarePositionsForRotatedShape(activeShapeType, tetrominoAngle)) { // First check to see if the tetromino is at the left of // the playing field if (tetrominoHorzPos + oldCell.x <= 0) { updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); return; } // Now check to see if another tetromino is preventing it from moving left if (fieldCells[((a + oldCell.y) * NUM_COLS + tetrominoHorzPos - 1 + oldCell.x) as Integer] <> TetrisShapeType.NONE) { updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); return; } } tetrominoHorzPos--; updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); } // TODO: Refactor with moveLeft method public function moveRight():Void { if (stopDropping) {return;} // Don't try to process if a piece isn't falling updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, true); for (oldCell in TetrisShapeType.squarePositionsForRotatedShape(activeShapeType, tetrominoAngle)) { // First check to see if the tetromino is at the left of // the playing field if (tetrominoHorzPos + oldCell.x >= NUM_COLS - 1) { updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); return; } // Now check to see if another tetromino is preventing it from moving left if (fieldCells[((a + oldCell.y) * NUM_COLS + tetrominoHorzPos + 1 + oldCell.x) as Integer] <> TetrisShapeType.NONE) { updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); return; } } tetrominoHorzPos++; updateFieldCells(tetrominoHorzPos, a, tetrominoAngle, false); }

/** * Keeps the fieldCells sequence (that represents the * cells in the playing field) updated to reflect reality. * pass in the row and column that the pivotal block was * located, as well as the former angle of the tetromino. * The new row, column, angle, as well as the shape type, is * held in the model, so don't have to be passed in. */ public function updateFieldCells(oldX:Integer, oldY:Integer, oldAngle:Integer, remove:Boolean):Void { // Place (or remove) the shape on the playing field for (oldCell in TetrisShapeType.squarePositionsForRotatedShape(activeShapeType, oldAngle)) { fieldCells[(oldY + oldCell.y) * NUM_COLS + oldX + oldCell.x] = if (remove) TetrisShapeType.NONE else activeShapeType; } }

public function canMoveDown(oldX:Integer, oldY:Integer, oldAngle:Integer):Boolean { var retVal = true; for (oldCell in TetrisShapeType.squarePositionsForRotatedShape(activeShapeType, oldAngle)) { // First check to see if the tetromino is at the bottom of // the playing field if (oldY + oldCell.y >= NUM_ROWS) { return false; } // Now check to see if another tetromino is preventing it from moving down if (fieldCells[(oldY + oldCell.y) * NUM_COLS + oldX + oldCell.x] <> TetrisShapeType.NONE) { return false; } } return true; }

/** * A sequence containing positions of each square in a * tetromino type. The first element in the sequence is * the one around which the tetromino will rotate. * Note that the "O" tetromino type doesn't rotate. */ public attribute squarePositions:Point[];

In the previous post, I showed you one of the two ways that the JavaFX Script compiler team has provided for creating compiled JavaFX Script applets. Today's example program demonstrates the second of these ways, by converting the Binding to a Function example into a compiled JavaFX Script applet. Here's a screenshot of the JavaFX Script applet, running in Firefox 3:

Declaratively express the JavaFX applet using the Applet object as the top level (see BindToFunctionApplet2.fx below).

Assign javafx.ui.Applet to the code attribute of the applet element in the HTML file (see BindToFunctionAppletPage2.html below).

Specify an AppletClass parameter with the name of the class in which the applet is expressed (in this case BindToFunctionApplet2, see the HTML file below).

Also, this approach requires that the applet be signed, so in this post I'm going to walk you through the process of creating a JAR file that contains the applet classes, and signing each of the JAR files.

Compare the following code with the Binding to a Function example, the major difference being the absence of the Frame object.
It isn't required since the applet provides its own UI container.

/* * BindToFunctionApplet2.fx - A compiled JavaFX program that demonstrates * a "JavaFX-like" way of creating compiled JavaFX applets. * It also demonstrates binding to a function. * * Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com) * to serve as a JavaFX Script example. */

You'll need to put the JAR files (shown in the archive tag above) in
the same directory as the HTML file. You can get these files from the
compiler download in the dist/lib directory. To compile this applet, I used the -target parameter so that it will run within browsers that have JRE 5 or later:

javafxc -target 1.5 BindToFunctionApplet2.fx

As I mentioned previously, you'll need to create a JAR file that contains the class files for this applet. To do this, I used the following command at my operating system prompt:

jar cvf BindToFunctionApplet2.jar BindToFunctionApplet2*.class

You'll also need to sign the JAR file that contains the applet classes, as well as the javafxrt.jar and Scenario.jar files. To do this, you'll need a signature key, which you can create with the following command:

The JavaFX Script compiler team has provided two ways of creating compiled JavaFX Script applets. Today's example program demonstrates one of these ways, by converting the Binding to a Function example into a compiled JavaFX Script applet. Here's a screenshot of the JavaFX Script applet, running in Firefox 3:

This way of creating a JavaFX Applet is more "Java-like" in that it uses the approach of extending the javafx.ui.Applet class. Notice that it overrides a couple of applet life-cycle functions to demonstrate that ability. Compare the following code with the Binding to a Function example, the major difference being the absence of the Frame object. It isn't required since the applet provides its own UI container.

/* * BindToFunctionApplet1.fx - A compiled JavaFX program that demonstrates * one way of creating compiled JavaFX applets. * It also demonstrates binding to a function. * * Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com) * to serve as a JavaFX Script example. */

You'll need to put the JAR files (shown in the archive tag above) in
the same directory as the HTML file. You can get these files from the
compiler download in the dist/lib directory. To compile this applet, I used the -target parameter so that it will run within browsers that have JRE 5 or later:

javafxc -target 1.5 BindToFunctionApplet1.fx

To execute this compiled JavaFX Script applet, either open the BindToFunctionAppletPage1.html file in a browser, or use appletviewer as shown below:

appletviewer BindToFunctionAppletPage1.html

Enjoy, and please post a comment if you have any questions! The next post will cover the other way of creating compiled JavaFX Script applets.