Building GUI Applications With JavaFX

Lesson 8: Bringing Interactivity to GUI Elements

By Dmitry Bondarenko and Alla Redko  
 
Version: JavaFX 1.3 « Previous 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Next »
 
Are you ready to bring some interactivity to your application? This lesson shows you how to add behavior to an application by following a step-by-step procedure. When you understand the concept, you can proceed to create more sophisticated interactive applications.

The JavaFX Script programming language enables you to make a desktop or a mobile application respond in a preprogrammed way to events through a convenient event-handling mechanism. Each JavaFX object that can potentially expose behavior has instance variables that map to event-related functions. You can define these functions to handle events such as the click of a mouse button, tap of a stylus, or the release of a key. For example, you can define a function that will render text when you click a circle with your mouse. For a complete list of events that can be handled by objects, see the JavaFX Script API.

This lesson is divided into two sections: Common Profile and Desktop Profile. The Common Profile section describes how to handle events in applications that run on mobile devices and on desktop platforms. The Desktop Profile section describes how to handle events on a desktop. It offers a demo that draws a button and uses rich effects that are specific only to the desktop platform.

Common Profile

Adding Graphics
Handling the Press Event
Handling the Release Event
Handling the Drag Event
 

This section describes how to handle events in mobile and desktop applications by using the common profile of API. The application demonstrates an animated "Play-Pause" button that changes its appearance as you perform various mouse actions. Then, when you press or click the button, it fades. If you release the mouse button, its indicator changes from "Play" to "Pause." If you click the button again, the "Pause" indicator switches back to "Play." You can also drag and drop the button within the scene. Try the following applet to evaluate event handling. Press, release, and drag the button.


The following screen captures show all the possible button states.

All possible states of the button
Figure 1: States of the Button
 

Create a file with an .fx extension, for example Events.fx. Avoid using file names that match the names of existing classes, instance variables, or reserved words because this type of naming leads to errors during compilation. For more information about existing classes, variables, and reserved words, see JavaFX Script API and Learning the JavaFX Script Programming Language.

Adding Graphics

All button states are available in the following PNG images: play_onMouseExited.png, play_onMousePressed.png, stop_onMouseExited.png, and stop_onMousePressed.png. To use the images as scene objects, use a combination of the Image and ImageView classes.

  1. Add import statements for the Image and ImageView classes.

  2. Define four Image objects corresponding to different states of the button.

    When specifying the image url, you can set the URI of the resource or use the relative codebase. In this example, the image url is set by using the __DIR__ variable that indicates the directory where the image is located. By default it points to the current directory, so ensure that the images are located in the same directory as application-compiled classes. To run the application on the mobile emulator ensure that the images are packed into the application jar file along with the compiled classes.

  3. Define the image variable to store an image of the current button state, and set it to the initial state, playNormal.

  4. Define the button variable to the a scene object corresponding to the current state of the button, and bind it to the image variable.

  5. Specify the scene of the application and add the button to its content.

    import javafx.stage.Stage;
    import javafx.scene.Scene;
    import javafx.scene.Group;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    
    def playNormal = Image { url: "{__DIR__}play_onMouseExited.png"};
    def playPressed = Image { url: "{__DIR__}play_onMousePressed.png"};
    def stopNormal = Image { url: "{__DIR__}stop_onMouseExited.png"};
    def stopPressed = Image { url: "{__DIR__}stop_onMousePressed.png"};
    
    var image = playNormal;
    
    var button = ImageView {image: bind image}
    
    Stage {
       title: "Play Button"
       scene: Scene {
           width: 300
           height: 240
           content: Group {
               content: button
           }   
        }
    }	
    
     

Note: The button is added to the Group construct for further application enhancements. In your application you can add an ImageView object directly to the scene content..
 
Handling the Press Event

This application handles three types of mouse events: mouse is pressed, mouse is released, and mouse is dragged. Each of these events is processed by a specific Node function. Because the ImageView class inherits all Node instance variables and functions, you can apply the onMousePressed, onMouseReleased, and onMouseDragged function to your button.

The press event happens when you press the button with a stylus or a mouse without releasing it. In this example, the press event changes the button's appearance.

To handle the mouse press event, perform the following steps.

  1. Define a Boolean variable named mode to fix whether the button is in the Play or Stop mode. Set the true value so that the button will be in Play mode when the application is started.

  2. Define the X and Y variables to use them for calculating the button's position when processing the drag event.

  3. Use the if construction to check the mode of the button. If it is in Play mode, the image is set to playPressed, and stopPressed otherwise.

    var mode = true; //true for the Play mode, 
                     //false for the Stop mode
    
    var X: Number;
    var Y: Number;
    
    onMousePressed: function(event) {
                   X = event.sceneX - event.node.translateX;
                   Y = event.sceneY - event.node.translateY;
                   image = if (mode){
                       playPressed;
                   } else {
                       stopPressed;
                   };
    }
    
     

As a result, the button changes its appearance when you press it depending on the mode of the button.

Handling the Release Event

The release event occurs when you release a mouse pointer from the button. In this example, when you release the pointer from the button, it switches its mode.

To handle the mouse release event, use the following code:

onMouseReleased: function(event) {
               if (mode){
                   image = stopNormal;
                   mode = false;
               } else {
                   image = playNormal;
                   mode = true;
               }
}
 

After an image is changed, the mode variable changes its value to the opposite. At this point, the application implements switching between the Play and Stop modes and changing the button appearance on the mouse press. The next section concerns dragging the button within the scene.

Handling the Drag Event

Unlike the mouse events mentioned in the previous sections, the onMouseDragged event does not change the button's appearance. This event enables you to alter the button position dragging it when the mouse is pressed. In this example, you cannot move the button beyond the bounds of the scene.

Use the following code fragment to handle the drag event:

onMouseDragged: function(event) {
               if (event.sceneX - X <0) {
                   event.node.translateX = 0;
               } else { if (event.sceneX - X > 300 - image.width){
                   event.node.translateX = 300 - image.width;
               } else {
                   event.node.translateX = event.sceneX - X;
               }
               }
               if (event.sceneY - Y <0) {
                   event.node.translateY = 0;
               } else {if (event.sceneY - Y > 240 - image.height){
                   event.node.translateY = 240 - image.height;
               } else{
                   event.node.translateY = event.sceneY - Y;
               }
               }
}
 

Two if constructions are used to check whether the button has been dragged outside of the scene, and to set values for the translateX and translateY variables of the button. Consider an example when the drag event occurred at the point (320, 100). Suppose that the X was fixed as 5, and the Y was fixed as 10. Then the event.node.translateX - X = 320 - 5 = 315, while 300 - image.width = 300 -63 = 237. The value 315 is greater than 237, so the translateX variable will be set to 300 - image.width = 237, and the button will be placed at the right border of the scene. The translateY variable will be set to event.sceneY - Y = 100 - 10 = 90. Therefore, the button will be moved to the following position: translateX = 237, translateY = 90.

The following screen capture shows the application run on the Touch Phone emulator.

Event Handling on Mobile Devices
Figure 2: Event-Handling Application Run on the Mobile Emulator
 

You can find the complete code of this application in the EventsCommon.fx file. The next section discusses how to handle some additional desktop-specific events.

Desktop Profile

Adding Tooltips
Handling the Mouse-Enter Event
Handling the Mouse-Exit Event
 

In this section, you will learn how to enhance the existing example by using additional features specific for desktop applications. Consider handling the mouse-enter and mouse-exit events by changing the button's appearance when the mouse pointer is on it. In addition, the modified application will show two variants of tooltips depending in the mode of the button. Try the following applet to evaluate event handling. Hover, press, release, and drag the button.



Two new images represent the button's state: play_onMouseEntered.png and stop_onMouseEntered.png. So you need to define two more Image variables.

def playHover = Image { url: "{__DIR__}play_onMouseEntered.png"};
def stopHover = Image { url: "{__DIR__}stop_onMouseEntered.png"};
 
Adding Tooltips

Perform the following steps to create a textual tooltip and add it to the scene.

  1. Add import statements for the Text, Font, Timeline, and Color classes.

  2. Declare a Text object specifying the string to render, the color, and the position of the tooltip. Set the content variable depending on the current mode of the button. Set "Play Button" when the button is in Play mode, and "Stop Button" otherwise. Also bind the translateX and translateY variables to the corresponding variables of the button object.

  3. Add the tooltip object to the scene.

    import javafx.stage.Stage;
    import javafx.scene.Scene;
    import javafx.scene.Group;
    import javafx.animation.Timeline;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.paint.Color;
    import javafx.scene.text.Font;
    import javafx.scene.text.Text;
    
    def tooltip = Text {
       content: bind if (mode) "Play Button" else "Stop Button"
       translateX: bind button.translateX
       translateY: bind button.translateY + 80
       opacity: 0.0
       font: Font {
           size: 12
           name: "Tahoma"
       }
       fill: Color.BLACK
    };
    
    Stage {
       title: "Play Button"
       scene: Scene {
           fill: Color.WHITE
           width: 300
           height: 240
           content: Group {
               content: [button, tooltip]
        }
    }	
    
     
Handling the Mouse-Enter Event

This event happens when you position the mouse pointer in the button area. This event is controlled by the onMouseEntered function.

To handle the mouse enter event define the onMouseEntered function. Create a Timeline object to alter the opacity of tooltips so that they do not appear instantly as you hover the button, but gradually within five seconds. The following code fragment performs these tasks.

def appear = Timeline {
   keyFrames: [
   at(0s) {tooltip.opacity => 0.0},
   at(0.5s) {tooltip.opacity => 1.0}
   ]
}

...

onMouseEntered: function(event) {
               image = if (mode){
                   playHover;
               } else {
                   stopHover
               }
               appear.rate = 1;
               appear.play();
}
 

After you enter the button area with the mouse pointer, the playHover or stopHover image appears and an animation timeline starts adding the tooltip. For more information about the onMouseEntered function, see JavaFX Script API. For more information about the MouseEvent class, see JavaFX Script API. For more information about animation, see Creating Animated Objects.


Note: Because a new state was introduced, you need to apply the following code for the onMouseReleased function. When the mouse is released the button should be in a hovered state, not in the initial state.

onMouseReleased: function(event) {
               if (mode){
                   image = stopHover;
                   mode = false;
               } else {
                   image = playHover;
                   mode = true;
               }
}

 
Handling the Mouse-Exit Event

This type of event occurs when the mouse pointer exits the button area. The event is defined by the onMouseExited function.

To define the mouse-exit event, use the following code:

onMouseExited: function(event) {
               image = if (mode){
                   playNormal;
               } else {
                   stopNormal
               }
               appear.rate = -1;
               appear.play();
}
 

When the mouse pointer exits the area defined by the graphic button, the button's appearance returns to its initial state. The tooltip disappears, because the rate variable of the animation timeline is set to -1 and the tooltip opacity alters from 1.0 to 0.0.

For more information about the onMouseExited function, see JavaFX Script API.

Here is the complete Events.fx file.

Conclusion

The JavaFX SDK provides a full range of built-in functions that handle events both in mobile and desktop applications. This example shows how easily some of them can be used to make your application respond to events generated by user actions.

« Previous 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Next »
 
 
 
 
 Photo of Dmitry Bondarenko
Dmitry Bondarenko is a technical writer for Sun Microsystems. He writes tutorials and articles for Java SE and JavaFX. Dmitry translates and edits articles and news for Java SE and JavaFX sections at Russian Sun Developers Network portal.
 
 Photo of Alla Redko
Alla Redko is a technical writer at Sun. She develops documentation for Java SE and JavaFX. Prior to her assignment to Sun, she worked as a technical writer for 10 years.