Tải bản đầy đủ
1 JavaFX Mobile—What Does It Mean?

1 JavaFX Mobile—What Does It Mean?

Tải bản đầy đủ

316

Chapter 10 Mobile Applications

Figure 10.1 is the big picture for JavaFX. This block diagram shows the various pieces
of JavaFX you can leverage when creating applications. As the diagram shows, there
are extensions for three environments: Desktop, Mobile and TV. All three environments support the common API (labeled Common Elements in Figure 10.1). As of this
writing, the runtime for the JavaFX TV environment does not yet exist. There is a runtime for the JavaFX mobile environment, which supports the common API only.
Mobility Tip
The JavaFX Swing components and the javafx.scene.effect package are not included in the
Common Elements. However, the new JavaFX “native” UI components are in the Common
Elements and can be used in mobile applications.

Application Framework
JavaFX
Desktop
Runtime

JavaFX
Mobile
Runtime

JavaFX
TV
Runtime

Desktop
Extensions

Mobile
Extensions

TV
Extensions

Common Elements

JavaFX Runtime

Java Virtual Machine (Java Plug In)

Figure 10.1 JavaFX Platform

JavaFX Mobile—What Does It Mean?

317

Table 10.1 summarizes the current limitations of JavaFX in the mobile environment.
Note that some of these limitations are due to the nature of mobile handsets (such as
mouse events and applet-specific features).
TABLE 10.1 Limitations in the Mobile Environment
Feature

Not Available with Mobile Runtime

javafx.scene.Cursor

Cursor property (no effect)

javafx.ext.Swing

Swing UI components

javafx.reflect

Provides reflective values

javafx.scene.effect

Creates visual effects

javafx.scene.effect.light
ShapeIntersect

Composite shapes

ShapeSubtract
AppletStageExtension

Provides browser-specific behavior

FXEvaluator

From javafx.util

package

StringLocalizer
Mouse Events

onMouseEntered (not relevant), general mouse
movements (such as hover, not recorded)

Our first step is to create a simple JavaFX application and deploy it with the JavaFX
mobile emulators. We’ll start with an application that displays information about its
runtime environment.

Mobile Emulator
To deploy this application with the emulator in NetBeans, right click the project name,
select the Properties menu and choose Run. From the radio button choices under
Application Execution Model, select Run in Mobile Emulator, as shown in
Figure 10.2.

Figure 10.2 JavaFX running in the Mobile Emulator

318

Chapter 10 Mobile Applications

There are currently three JavaFX mobile emulators: Default (DefaultFxPhone1), Touch
(DefaultFxTouchPhone1), and Qwerty (QwertyFxPhone1). Figure 10.3 and Figure 10.4
show our simple application running in the standard mobile emulators. In
Figure 10.3, View A shows the default emulator and View B shows the touch emulator. Figure 10.4 shows the qwerty emulator.
This application includes a Text component that displays information about the current environment (screen size and profile). Knowing something about the current
environment is useful for applications that target both desktop and mobile profiles. A
Rectangle shape acts as a background for the Text component.

View B: DefaultFxTouchPhone1

View A: DefaultFxPhone1

Figure 10.3 JavaFX mobile emulator (default emulators)

JavaFX Mobile—What Does It Mean?

319

Figure 10.4 JavaFX mobile emulator (qwerty emulator)

Discovering Your Environment
JavaFX provides several ways for applications to discover their execution environment.

__PROFILE__
The pseudo variable __PROFILE__ is set to "browser" (running as an applet in a
browser), "mobile" (running in the mobile environment), or "desktop" (not an applet
and not mobile). Here, Boolean isMobile is true in a mobile environment.
def isMobile = __PROFILE__ == "mobile";

FX.getProperty
Object FX is available in all JavaFX applications. FX function getProperty returns system-level properties based on a key String argument. In a mobile environment, key
argument "javafx.me.profiles" returns information on the JavaFX runtime version,
as well as the mobile runtime version. In a non-mobile environment, FX.getProperty
with this key returns null.
def isMobile = FX.getProperty("javafx.me.profiles") != null;

320

Chapter 10 Mobile Applications

Stage
The Stage object reflects the mobile device screen height and width.
def stage: Stage = Stage {
title: "Mobility"
scene: Scene {
content: [ . . . ]
}
}
def height = stage.height;
def width = stage.width;

// height of screen
// width of screen

Listing 10.1 is the code for the application running in Figure 10.3 and Figure 10.4.
After determining whether or not the environment is the mobile runtime, the application builds a profile string. This variable (profileString) includes the screen dimensions and the __PROFILE__ pseudo variable.
Next, the application defines a background Rectangle (r1) and a Text component (t1).
The background rectangle binds its dimensions to expressions that depend on the
Stage object stage. The Text object sets its location relative to the background rectangle and binds property wrappingWidth to an expression using the rectangle’s width.
Thus, the dimensions of the background rectangle and the Text wrappingWidth depend
on the screen size.
The scene graph includes a Group object that holds the Text and Rectangle. The
Groups’s horizontal position depends on the Stage width. Grouping Text and Rectangle together makes it easy to maintain the relative position of these two components.
Listing 10.1 MobileTest1
def isMobile = __PROFILE__ == "mobile";
def profileString:String = bind "height={stage.height}, width={stage.width}.\n"
"profile={__PROFILE__}.";
def r1 = Rectangle {
height: bind stage.height / 2
width: bind stage.width - 50
fill: Color.web("#3333ee", .3)
stroke: Color.CADETBLUE
}
def t1 = Text {
x: r1.x + 15
y: r1.y + 15
wrappingWidth: bind r1.width - 30
font: Font {
size: 16
}

JavaFX Mobile—What Does It Mean?

321

textOrigin: TextOrigin.TOP
content: bind profileString
}
def stage: Stage = Stage
title: "Mobility"
scene: Scene {
content: [
Group {
layoutX:
layoutY:
content:
}
]
}
}

{

bind stage.width / 2 - r1.layoutBounds.width / 2
20
[ r1, t1 ]

Orientation Changes
When a mobile device changes orientation, you may want your application to
respond. This means updating your application’s scene graph. Fortunately, JavaFX
makes detecting changes in orientation straightforward with binding expressions
and/or triggers. Perhaps the more difficult part in responding to orientation changes
is deciding what components in your scene graph should change and how.
For example, in the mobility test application from Figure 10.3, the text component
should not rotate (you want the text to remain readable), but the Text object literal’s
dimensions should expand or contract depending on the dimensions of the display.
Figure 10.5 shows an enhanced version of our mobility test application. View A
shows the application running in the emulator with normal (0 degree rotation) orientation. The text display includes the word “vertical” at the bottom. View B shows the
emulator with a 90-degree rotation, giving a horizontal orientation to the display. You
can see that the text component wrapping width and the background rectangle adjust
to the change in screen size. The text now includes the word “horizontal” at the bottom.

322

Chapter 10 Mobile Applications

horizontal
vertical
View B: Horizontal Orientation

View A: Vertical Orientation

Figure 10.5 Responding to orientation changes

Listing 10.2 shows the code that responds to these orientation changes. To display the
word “vertical” with a vertical display and the word “horizontal” with a horizontal
display, we define a new String variable (orientationString) that includes the correct
orientation word. The program updates this String using an on replace block that triggers when object stage.height changes. (The dimensions of object stage reflect
changes in orientation.) If the height is greater than the width, the phone is oriented
vertically and String orientationString is updated with the correct label.
Text component t1 now binds its content property to orientationString so that when
the orientation changes, the scene graph reflects the new content. Because property
wrappingWidth is already bound to the background rectangle’s width (which is bound
to stage.width), the text reformats to fit into the new dimensions.
Listing 10.2 MobileTest2
def isMobile = __PROFILE__ == "mobile";
def profileString:String = bind "height={stage.height}, width={stage.width}.\n"
"profile={__PROFILE__}.";
var orientationString: String;
def height = bind stage.height on replace {
if (stage.height > stage.width) {

JavaFX Mobile—What Does It Mean?

323

orientationString = "{profileString}\nvertical";
}
else {
orientationString = "{profileString}\nhorizontal";
}
}
def r1 = Rectangle {
height: bind stage.height / 2
width: bind stage.width - 50
fill: Color.web("#3333ee", .3)
stroke: Color.CADETBLUE
}
def t1 = Text {
x: r1.x + 15
y: r1.y + 15
wrappingWidth: bind r1.width - 30
font: Font {
size: 16
}
textOrigin: TextOrigin.TOP
content: bind orientationString
}
def stage: Stage = Stage {
. . . code unchanged . . .
}

Mobility Tip
Group components together that respond to orientation changes in the same way. This will
simplify your program. For example, Text components are not typically rotated, but you may
need to resize them when the display size changes. Similarly, ImageView is also not rotated but
may need scaling. Non-text based graphical objects may require a 90-degree rotation.

Mouse and Key Events
The default emulator includes a keypad with a selection button, arrow keys, soft keys,
and numeric keys. The default touch phone emulator has no keypad, but includes a
virtual keypad with the TextBox component. The mouse mimics touch gestures,
which should be viewed as an approximation of real-phone behavior. For example,
touch gestures require nice, fat targets, whereas mouse devices can point with precision on the screen.

324

Chapter 10 Mobile Applications

Let’s update the mobile test program to change the rectangle’s background color with
either a mouse click (emulating the touch phone’s touch gesture) or a keypad select
key (on the default emulator).
Figure 10.6 shows the default emulator with most of the keys labeled.

VK_SOFTKEY_0

VK_UP

VK_SOFTKEY_1
VK_ENTER

VK_CLEAR

VK_SOFTKEY_2

VK_LEFT
VK_RIGHT

VK_1

VK_DOWN

VK_9
VK_ASTERISK

VK_POUND

Figure 10.6 Mobile emulator key press events

Figure 10.7 shows the qwerty emulator with several keys labeled.
The onKeyPressed property lets you specify a key press event handler, as shown here.
onKeyPressed: function(ke: KeyEvent): Void {
println("keycode={ke.text}, code={ke.code}");
if (ke.code == KeyCode.VK_ENTER)
bgColor = if (bgColor == Color.BISQUE) Color.THISTLE else Color.BISQUE
}

The println statement displays the key text and key code for each key press.
(Figure 10.6 and Figure 10.7 label key press codes.) If the key code is VK_ENTER, the

JavaFX Mobile—What Does It Mean?

325

VK_RIGHT
VK_SOFTKEY_1

VK_SOFTKEY_0
VK_DOWN

VK_Z

VK_ENTER

VK_SOFTKEY_2
VK_M

Figure 10.7 Mobile emulator (qwerty) key press events
background color bgColor alternates between two colors (Color.THISTLE and
Color.BISQUE).
Before key events can be detected, the node that has the key press event handler must
have focus. Rectangle r1 gains focus with the following statement.
r1.requestFocus();

Property onMouseClicked lets you change background colors with the touch phone
emulator. Here’s the mouse event handler.
onMouseClicked: function(e: MouseEvent): Void {
bgColor = if (bgColor == Color.BISQUE) Color.THISTLE else Color.BISQUE
}

Listing 10.3 shows the object literal for Rectangle r1 with both the key press and
mouse click event handler.
Listing 10.3 Key Press Events
var bgColor = Color.BISQUE;
def r1 = Rectangle {
height: bind stage.height / 2
width: bind stage.width - 50
fill: bind bgColor
stroke: Color.CADETBLUE

326

Chapter 10 Mobile Applications

onKeyPressed: function(ke: KeyEvent): Void {
println("keycode={ke.text}, code={ke.code}");
if (ke.code == KeyCode.VK_ENTER)
bgColor = if (bgColor == Color.BISQUE) Color.THISTLE else Color.BISQUE
}
onMouseClicked: function(e: MouseEvent): Void {
println("mouse clicked");
bgColor = if (bgColor == Color.BISQUE) Color.THISTLE else Color.BISQUE
}
}
def t1 = Text { . . . }
def stage: Stage = Stage
title: "Mobility"
scene: Scene {
content: [
Group {
layoutX:
layoutY:
content:
}
]
}
}
r1.requestFocus();

{

bind stage.width / 2 - r1.layoutBounds.width / 2
20
[ r1, t1 ]

User Input
The JavaFX UI component TextBox gathers text-based user input. When the component has focus, the emulator accepts keyboard input, as shown in Figure 10.8. Select
the Enter key to confirm input. With the QwertyFxPhone1 emulator, use the emulator’s keypad for input.