« Rolling Your Own JavaFX "Custom Nodes": A Graphical Menu Example | Main | Progress Indicator: Creating a JavaFX Custom Node and Binding to a Model »

July 28, 2008

Getting Decked: Another JavaFX Custom Node

In the Rolling Your Own JavaFX "Custom Nodes": A Graphical Menu Example post, I began showing you how to create your own UI controls in JavaFX.  I also said that the JFX Custom Nodes category has been started in this blog in which custom nodes that you and I create will be featured.

Today's example features a simple custom node, named DeckNode, that I developed as a way to store a set of Node instances and display one of these nodes at a time.  It is being used to augment the graphical menu example by showing the Node that pertains to a given menu button.  Note that the concept is very similar to the Java CardLayout, so I wouldn't be surprised to see a similar class appear in the javafx.scene.layout package at some point.  Just for grins, I provided the ability to specify a fade-in duration for the node being displayed.  Here's a screenshot of the example application, with one of the nodes in the "deck" being displayed as the splash page: 

Decknodeexample_5  

One of the goals of JavaFX is for graphics designers and developers to be able to work together effectively in creating great looking applications.  To demonstrate this, Mark Dingman (the User eXperience director at Malden Labs) created some graphical mock-ups for the fictitious CD application shown above.  Future blog posts will turn these mock-ups into functioning pages using a combination of UI controls from the JavaFX SDK and custom nodes that we create in this blog.  Here's an example mocked-up page in the form of a graphic that is being displayed as a result of pressing the Play button:

Decknodeexample_play

By the way, Malden Labs, under CTO Thom Theriault's technical leadership, has made a strong commitment to using JavaFX for some pretty exciting systems in the works.  Anyway, if you would like to try today's example out, click on this Java Web Start link, keeping in mind that you'll need at least JRE 5.  Also, installing Java SE 6 update 10 will give you faster deployment time.

Webstartsmall2

Here's the code for the DeckNode class, contained in a file named DeckNode.fx:

/*
 *  DeckNode.fx - 
 *  A node that shows a deck of nodes one at a time.
 *
 *  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
 *  to demonstrate how to create custom nodes in JavaFX
 */

package com.javafxpert.custom_node;
 
import javafx.animation.*;
import javafx.lang.*;
import javafx.scene.*;

/**
 *  A node that shows a deck of nodes one at a time.  When the
 *  visibleNodeId attribute is assigned a value, the Node whose
 *  id is the same as the name becomes visible.  Note that the
 *  the id attribute of each Node must be assigned a unique name.
 *  This class also has an attribute in which a fade-in duration
 *  may be specified.
 */
public class DeckNode extends CustomNode { 
  /**
   * A sequence that contains the Node instances in this "deck"
   */
  public attribute content:Node[];
  
  /**
   * An optional node that will appear in the background of all nodes
   */
  public attribute backgroundNode:Node;

  /**
   * The id of the node that is to be visible
   */
  public attribute visibleNodeId:String on replace {
    var nodes = for (node in content where node.id == visibleNodeId) node;
    visibleNodeRef = if (sizeof nodes > 0) nodes[0] else null;
    fadeTimeline.start(); 
  }
   
  /**
   * The amount of time to fade-in the new Node 
   */
  public attribute fadeInDur:Duration = 1ms;

  /**
   * This attribute is interpolated by a Timeline, and the opacity
   * attribute of this DeckNode class is bound to it.  This helps
   * enable the fade-in effect.
   */
  private attribute opa:Number;

  /**
   * Override the opacity attribute so that it can be bound to the
   * opa attribute that is interpolated by a Timeline
   */
  override attribute opacity = bind opa;

  /**
   * A Timeline to control the fade-in behavior
   */
  private attribute fadeTimeline =
    Timeline {
      keyFrames: [
        KeyFrame {
          time: bind fadeInDur
          values: [
            opa => 1.0 tween Interpolator.LINEAR,
          ]
        }
      ]
    };

  /**
   * A reference to the Node in the Node instances that is visible
   */
  protected attribute visibleNodeRef:Node;
   
  /**
   * Create the Node
   */
  public function create():Node {
    Group {
      content: bind [
        backgroundNode,
        visibleNodeRef
      ]
    };
  }
}  
 

Shown below is the main script for this program, in a file named DeckNodeExampleMain.fx:

/*
*  DeckNodeExampleMain.fx -
*  An example of using the DeckNode custom node.  It also demostrates
*  the MenuNode and ButtonNode custom nodes
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to demonstrate how to create custom nodes in JavaFX
*/
package com.javafxpert.deck_node_example.ui;

import javafx.application.*;
import javafx.scene.*;
import javafx.scene.geometry.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import javafx.scene.transform.*;
import java.lang.System;
import com.javafxpert.custom_node.*;

var deckRef:DeckNode;

Frame {
  var stageRef:Stage;
  var menuRef:MenuNode;
  title: "DeckNode Example"
  width: 500
  height: 400
  visible: true
  stage:
    stageRef = Stage {
      fill: Color.BLACK
      content: [
        deckRef = DeckNode {
          fadeInDur: 700ms
          content: [
            Group {
              var vboxRef:VBox;
              var splashFont =
                Font {
                  name: "Sans serif"
                  style: FontStyle.BOLD
                  size: 12
                };
              id: "Splash"
              content: [
                ImageView {
                  image:
                    Image {
                      url: "{__DIR__}images/splashpage.png"
                    }
                },
                vboxRef = VBox {
                  translateX: bind stageRef.width - vboxRef.getWidth() - 10
                  translateY: 215
                  spacing: 1
                  content: [
                    Text {
                      content: "A Fictitious Audio Application that Demonstrates"
                      fill: Color.WHITE
                      font: splashFont
                    },
                    Text {
                      content: "Creating JavaFX Custom Nodes"
                      fill: Color.WHITE
                      font: splashFont
                    },
                    Text {
                      content: "Application Developer: Jim Weaver"
                      fill: Color.WHITE
                      font: splashFont
                    },
                    Text {
                      content: "Graphics Designer: Mark Dingman"
                      fill: Color.WHITE
                      font: splashFont
                    },
                  ]
                }
              ]
            },
            Group {
              id: "Play"
              content: [
                ImageView {
                  image:
                    Image {
                      url: "{__DIR__}images/playlist.png"
                    }
                }
              ]
            },
            Group {
              id: "Burn"
              content: [
                ImageView {
                  image:
                    Image {
                      url: "{__DIR__}images/burning.png"
                    }
                }
              ]
            },
            Group {
              id: "Config"
              content: [
                ImageView {
                  image:
                    Image {
                      url: "{__DIR__}images/config.png"
                    }
                }
              ]
            },
            Group {
              id: "Help"
              content: [
                ImageView {
                  image:
                    Image {
                      url: "{__DIR__}images/help.png"
                    }
                }
              ]
            }
          ]
        },
        menuRef = MenuNode {
          translateX: bind stageRef.width / 2 - menuRef.getWidth() / 2
          translateY: bind stageRef.height - menuRef.getHeight()
          buttons: [
            ButtonNode {
              title: "Play"
              imageURL: "{__DIR__}icons/play.png"
              action:
                function():Void {
                  deckRef.visibleNodeId = "Play";
                }
            },
            ButtonNode {
              title: "Burn"
              imageURL: "{__DIR__}icons/burn.png"
              action:
                function():Void {
                  deckRef.visibleNodeId = "Burn";
                }
            },
            ButtonNode {
              title: "Config"
              imageURL: "{__DIR__}icons/config.png"
              action:
                function():Void {
                  deckRef.visibleNodeId = "Config";
                }
            },
            ButtonNode {
              title: "Help"
              imageURL: "{__DIR__}icons/help.png"
              action:
                function():Void {
                  deckRef.visibleNodeId = "Help";
                }
            },
          ]
        }
      ]
    }
}

deckRef.visibleNodeId = "Splash";

Please notice the last line of the listing above is what causes the node that we named "Splash" to be displayed when the application starts. 

The code for the other custom nodes used in this program (ButtonNode.fx and MenuNode.fx) are in the Rolling Your Own JavaFX "Custom Nodes": A Graphical Menu Example post, so you may want to visit that post.  At some point I'll put these custom nodes in a JAR file if developers find them useful.  As always, please leave a comment if you have any questions of comments!

Thanks,
Jim Weaver

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00e54f133d69883400e553d255ce8834

Listed below are links to weblogs that reference Getting Decked: Another JavaFX Custom Node:

Comments

thanks for this great article.

Great Post! Thanks

betsson
casino

Thanks your comment.

Kirill,

I misspoke when I said that there is just one animation timeline. Each button has its own timeline, so when the mouse is moved back an forth quickly they seem to be sharing one.

I'd like to ask your advice, as well as the advice of other readers. What should the resizing behavior be, given that I'd like a button to work independently of a menu, as well as in a menu with other buttons? Ideas, anyone?

Thanks,
Jim Weaver

Kirill,

Thanks. Your observation that they share the same animation timeline is correct. I try to minimize the number of timelines in a program to optimize performance, but can see where it would be preferred for each button to have its own timeline instance. I'll give that a try. By the way, I really enjoy your Pushing Pixels www.pushing-pixels.org blog!

Thanks,
Jim Weaver

As you move the mouse over a button, it slowly grows (and then shrinks as you move the mouse out). Now, move the mouse quickly over the entire bottom row of icons and see that they seem to share the same animation timeline - instead of growing a little and then shrinking back as the mouse quickly passes over an icon, the entire row of icons grows together even when the mouse is no longer over that icon.

Kirill,

Thanks for testing the app. Can you please be more specific on the symptoms that you're experiencing?

Thanks,
Jim Weaver

Move the mouse quickly over the icons and see the incorrect jumping animations.

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been posted. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment

My Photo

Upcoming Speaking Engagements:


  • Stephen Chin and Jim Weaver speaking about JavaFX Platform

  • Speaking on JavaFX and Java at Øredev in Malmö, Sweden on 2-6 November, 2009

Upcoming JavaFX Training:


  • Developing Secure, Rich Internet Applications Hosted on a Variety of Clients Using JavaFX Technology

Enter your email address:

Delivered by FeedBurner

Available now as early access eBook


  • Click book image above to obtain eBook

Twitter Updates

    follow me on Twitter

    Affiliations:

    DZone Links:


    July 2009

    Sun Mon Tue Wed Thu Fri Sat
          1 2 3 4
    5 6 7 8 9 10 11
    12 13 14 15 16 17 18
    19 20 21 22 23 24 25
    26 27 28 29 30 31  

    Disclaimer:

    • By reading this site, you are agreeing that under no circumstances will Veriana Networks, Inc. or its affiliates be responsible for (1) any information contained on or omitted from the site, (2) any person's reliance on any such information, whether or not the information is correct, current or complete, (3) the consequences of any action you or any other person takes or fails to take, whether or not based on information provided by or as a result of the use of the sites.