Building GUI Applications With JavaFX

Lesson 3: Presenting UI Objects in a Graphical Scene


The JavaFX Script Programming language is based on a scene graph. The scene graph is a tree-like data structure which defines a hierarchy of graphical objects in a scene. A single element in the scene graph is called a node. Each node has one parent except for the root node, which has no parent. Each node is either a leaf node or a branch. A leaf node has no children. A branch node has zero or more children.

JavaFX nodes handle different types of content such as UI components, shapes, text, images, and media. Nodes can be transformed and animated. You can also apply various effects to nodes.

In this lesson, you will create an application with three nodes: a circle, text, and an image, as shown below.

The Nodes example after startup
Figure 1: The Nodes example

The application is created using the common profile API and can be run both on mobile devices and desktop platforms. If you want to learn more about the desktop platform API, refer to the JavaFX API. The screen captures provided in this lesson are taken from a desktop application.

JavaFX renders everything on a scene. You can think of the scene as a drawing surface for graphical content. The scene is a container that holds the scene graph nodes.

In any JavaFX GUI application, you create a scene and add nodes to it. You can modify the graphical scene by applying effects, transformations, and animation. The JavaFX runtime takes care of any changes in the graphical scene and performs any necessary repaints for you.

The javafx.scene.Node class is the base class for the scene graph nodes. All other node classes, for example javafx.scene.shape.Circle, inherit from the Node class. For a complete list of instance variable and functions, see the API documentation for the Node class.

The Node class defines a local coordinate system in which the X coordinate increases to the right, and the Y coordinate increases downwards.

Nodes can be changed by applying transformations such as translation, rotation, scaling, and shearing. For example, a translation moves the origin of the node's coordinate system along either the X or Y axis, or both. To define the translation, set the values for the translateX or translateY variables or both.

JavaFX provides powerful support for effects available through the javafx.scene.effect and javafx.scene.effect.light packages. You can see some of the applied effects as well as transformations in Quick JavaFX GUI Overview. Note that the packages are available only in the desktop profile API.

Nodes can receive mouse and keyboard events. You can define functions to be notified when such events occur. For details, see Bringing Interactivity to GUI Elements.

Nodes can be grouped together and treated as a single entity. If you need to provide common behavior for several nodes, group them, and define the required behavior for the whole group. The javafx.scene.Group class represents a group of nodes.

Now you will create a simple application as shown in Figure 1. The graphical scene of this application contains three nodes displayed below on separate windows. They are a shape object (a circle), text, and an image.

Three separate windows containing a circle, text, and an image.
Figure 2: Three nodes on separate windows: a circle, text, and an image

First you will add the nodes to the scene as separate nodes. Then you will group them and apply a transformation to the whole group.

Create an application window with the title "Nodes". For details, see Using Declarative Syntax. The following code creates the window.


Note: For the mobile version of the application, this step is required to define the scene.
import javafx.stage.Stage;

Stage {
title: "Nodes"
}

A scene is declared using the Scene object literal. You will set the scene's width to 220 pixels, its height to 170 pixels, and give it a light blue background.

  1. Add import statements for the javafx.scene.Scene and javafx.scene.paint.Color classes.
  2. Declare the Scene object literal.
  3. Define the fill variable to set the background for the scene.
  4. Define the width, and height attributes of the Scene object. This step is required only for the desktop version of the demo to specify the dimension of the application window. For details, see the Using Declarative Syntax lesson.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;

Stage {
title: "Nodes"
scene: Scene {
fill: Color.LIGHTBLUE
width: 220
height: 170
}
}

This code produces the following output.

A standard window whose scene has a light blue background
Figure 3: A scene with light blue background

You add a node to the scene by declaring this node as an element of the content of the scene. The content variable of the scene, which is a sequence of nodes, defines the graphical content of your application.

The first node is a circle. For details on the Circle class, see Using Declarative Syntax. You will paint the boundary of the circle with yellow color.

  1. Import the javafx.scene.shape.Circle class.
  2. Define the content variable of the scene.
  3. Add the Circle object literal to the content variable.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

Stage {
title: "Nodes"
scene: Scene {
fill: Color.LIGHTBLUE
width: 220
height: 170
content: Circle {
centerX: 50 centerY: 50 radius: 50
stroke: Color.YELLOW
fill: Color.WHITE
}
}
}

The modified code gives you the following output.

Window with light blue background and white circle with yellow boundary
Figure 4: A scene with a circle node

  1. Add an import statement for the javafx.scene.text.Text class.
  2. Add the Text object literal to the content variable.

By default the text node will be placed at the point (0,0), which means that the left bottom point of the first character will be placed at (0,0). For this reason, the text is not visible in the application window when the code is compiled and run. In the next step, the default location will be changed so that text is visible.

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;

Stage {
title: "Nodes"
scene: Scene {
width: 220
height: 170
fill: Color.LIGHTBLUE
content: [
Circle {
centerX: 50 centerY: 50 radius: 50
stroke: Color.YELLOW
fill: Color.WHITE
},
Text {
content: "Duke"
}
]//Content
}//Scene
}//Stage

Use square brackets to specify a sequence of nodes and commas to separate its elements.

You can change the default location by applying a rotation transformation. The rotation is specified by an anchor point and an angle. The node will be rotated clockwise around the anchor point by the specified angle.

To calculate the necessary values, look at Figure 5. If you take the point (10, 100) as an anchor point and draw a circle with a radius equal to the distance to the left bottom point of the text node, then part of this circle falls inside the circle node. Moving the text node along this circle by 33 degrees clockwise gives the result shown in Figure 5.

  1. Add an import statement for the javafx.scene.transform.Transform class.
  2. Define the transforms variable of the text node to rotate the node by 33 degrees around the point (10,100).
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.scene.transform.Transform;

Stage {
title: "Nodes"
scene: Scene {
width: 220
height: 170
fill: Color.LIGHTBLUE
content: [
Circle {
centerX: 50 centerY: 50 radius: 50
stroke: Color.YELLOW
fill: Color.WHITE
},
Text {
transforms: Transform.rotate(33, 10, 100)
content: "Duke"
}
]//Content
}//Scene
}//Stage

The modified code provides the following output.

Window with light blue background, white circle with yellow boundary, and text inside the circle
Figure 5: A scene with a circle and text nodes

JavaFX applications can display images that are stored either on the Internet or in the local directory of your computer. The images are displayed using the ImageView and Image classes, and the url variable which points to the location of the image. In case you use an image from the Internet, its url variable which indicates the URL, is specified as a URI. In case you refer to an image from the local directory, its url variable which indicates the path to the directory, is specified using the __DIR__ variable.

If you keep an image on the Internet, you need an Internet connection in order to display it in the application.

The example from this section uses an image of Duke from the Java Tutorials stored in the local directory. The example specifies the image using the __DIR__ variable. By default, it points to the current directory, so make sure that the image is located in the same directory as the application's compiled classes. To run the application on the mobile emulator, make sure that the image is packed into the application jar file along with the compiled classes.

By default, the left upper point of the image node is placed in the point (0,0). The dimensions of this image fit exactly into the area over the circle node.

For more information about the ImageView and Image classes, see the JavaFX API. For more details on the use of images, see Creating Animated Objects.

  1. Add import statements for the Image and ImageView classes from the javafx.scene.image package.
  2. Add the ImageView object literal to the content variable.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.scene.transform.Transform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

Stage {
title: "Nodes"
scene: Scene {
width: 220
height: 170
fill: Color.LIGHTBLUE
content: [
Circle {
centerX: 50 centerY: 50 radius: 50
stroke: Color.YELLOW
fill: Color.WHITE
},
Text {
transforms: Transform.rotate(33, 10, 100)
content: "Duke"
},
ImageView {
image: Image {url: "{__DIR__}dukewave.png"}
}
]//Content
}//Scene
}//Stage

You created an application whose graphical scene contains three nodes. The output is shown in the following image.

Window with text and image inside circle
Figure 6: A scene with three nodes

Now add the nodes to a group and then add the group to the content variable of the scene.

  1. Add an import statement for the javafx.scene.Group class.
  2. Modify the declaration of the content variable for the scene so that it contains the Group object literal.
  3. Move all nodes to the content variable of the Group. The code appears as follows.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.scene.transform.Transform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.Group;

Stage {
title: "Nodes"
scene: Scene {
width: 220
height: 170
fill: Color.LIGHTBLUE
content: Group {
content: [
Circle {
centerX: 50 centerY: 50 radius: 50
stroke: Color.YELLOW
fill: Color.WHITE
},
Text {
transforms: Transform.rotate(33, 10, 100)
content: "Duke"
},
ImageView {
image: Image {url: "{__DIR__}dukewave.png"}
}//ImageView
]//Content
}//Group
}//Scene
}//Stage

Note the importance of the order in which you add objects to your group. This order defines how those objects are laid out. If you add the circle node last (after the text and image nodes), then the circle will be drawn over the two other objects. Because the circle has a fill color, these nodes will not be seen.

Finally, define the translation for the group of nodes to move the group to the center of the window as shown in the following code.

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.scene.transform.Transform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.Group;

Stage {
title: "Nodes"
scene: Scene {
width: 220
height: 170
fill: Color.LIGHTBLUE
content: Group {
translateX: 55
translateY: 10
content: [
Circle {
centerX: 50 centerY: 50 radius: 50
stroke: Color.YELLOW
fill: Color.WHITE
},
Text {
transforms: Transform.rotate(33, 10, 100)
content: "Duke"
},
ImageView {
image: Image {url: "{__DIR__}dukewave.png"}
}//ImageView
]//Content
}//Group
}//Scene
}//Stage

This modification shifts all three nodes simultaneously as displayed in the following image.

The group of objects moved to center of window
Figure 7: A group of nodes shifted to the center of the window

For your reference, here is the complete code of this example application.

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Group;
import javafx.scene.shape.Circle;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.text.Text;
import javafx.scene.transform.Transform;

Stage {
title: "Nodes"
scene: Scene {
width: 220
height: 170
fill: Color.LIGHTBLUE
content: Group {
translateX: 55
translateY: 10
content: [
Circle {
centerX: 50 centerY: 50 radius: 50
stroke: Color.YELLOW
fill: Color.WHITE
},
Text {
transforms: Transform.rotate(33, 10, 100)
content: "Duke"
},
ImageView {
image: Image {url: "{__DIR__}dukewave.png"}
}//ImageView
]//Content
}//Group
}//Scene
}//Stage

0 comments:

Post a Comment