目录
概述
JavaFX是用于构建富互联网应用程序的Java库。使用JavaFX开发的应用程序可以在各种设备上运行,如台式计算机,手机,物联网
设备,平板电脑等。这一章主要是介绍如何应用JavaFX使用编程声明方式开发用户界面。
编程与声明创建用户界面
以节点为中心的UI的简介
package sample;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Rectangle2D;
import javafx.geometry.VPos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;
public class StageCoachMain extends Application {
StringProperty title = new SimpleStringProperty();
Text textStageX;
Text textStageY;
Text textStageW;
Text textStageH;
Text textStageF;
CheckBox checkBoxResizable;
CheckBox checkBoxFullScreen;
double dragAnchorX;
double dragAnchorY;
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
//设置舞台
StageStyle stageStyle = StageStyle.DECORATED;
List<String> unnamedParams = getParameters().getUnnamed();
if (unnamedParams.size() > 0) {
String stageStyleParam = unnamedParams.get(0);
if (stageStyleParam.equalsIgnoreCase("transparent")) {
stageStyle = StageStyle.TRANSPARENT;
} else if (stageStyleParam.equalsIgnoreCase("undecorated")) {
stageStyle = StageStyle.UNDECORATED;
} else if (stageStyleParam.equalsIgnoreCase("utility")) {
stageStyle = StageStyle.UTILITY;
}
}
final Stage stageRef = stage;
Group rootGroup;
TextField titleTextField;
Button toBackButton = new Button("toBack()");
toBackButton.setOnAction(e -> stageRef.toBack());
Button toFrontButton = new Button("toFront()");
toFrontButton.setOnAction(e -> stageRef.toFront());
Button closeButton = new Button("close()");
//关闭舞台并检测何时关闭
closeButton.setOnAction(e -> stageRef.close());
//绘制圆弧矩形
Rectangle blue = new Rectangle(250, 350, Color.SKYBLUE);
blue.setArcHeight(50);
blue.setArcWidth(50);
textStageX = new Text();
textStageX.setTextOrigin(VPos.TOP);
textStageY = new Text();
textStageY.setTextOrigin(VPos.TOP);
textStageH = new Text();
textStageH.setTextOrigin(VPos.TOP);
textStageW = new Text();
textStageW.setTextOrigin(VPos.TOP);
textStageF = new Text();
textStageF.setTextOrigin(VPos.TOP);
checkBoxResizable = new CheckBox("resizable");
checkBoxResizable.setDisable(stageStyle == StageStyle.TRANSPARENT
|| stageStyle == StageStyle.UNDECORATED);
checkBoxFullScreen = new CheckBox("fullScreen");
titleTextField = new TextField("Stage Coach");
Label titleLabel = new Label("title");
//使用UI布局容器
HBox titleBox = new HBox(titleLabel, titleTextField);
VBox contentBox = new VBox(
textStageX, textStageY, textStageW, textStageH, textStageF,
checkBoxResizable, checkBoxFullScreen,
titleBox, toBackButton, toFrontButton, closeButton);
contentBox.setLayoutX(30);
contentBox.setLayoutY(20);
contentBox.setSpacing(10);
rootGroup = new Group(blue, contentBox);
//用节点填充场景
Scene scene = new Scene(rootGroup, 270, 370);
//设置场景中背景
scene.setFill(Color.TRANSPARENT);
//when mouse button is pressed, save the initial position of screen
rootGroup.setOnMousePressed((MouseEvent me) -> {
dragAnchorX = me.getScreenX() - stageRef.getX();
dragAnchorY = me.getScreenY() - stageRef.getY();
});
//when screen is dragged, translate it accordingly
rootGroup.setOnMouseDragged((MouseEvent me) -> {
stageRef.setX(me.getScreenX() - dragAnchorX);
stageRef.setY(me.getScreenY() - dragAnchorY);
});
textStageX.textProperty().bind(new SimpleStringProperty("x: ")
.concat(stageRef.xProperty().asString()));
textStageY.textProperty().bind(new SimpleStringProperty("y: ")
.concat(stageRef.yProperty().asString()));
textStageW.textProperty().bind(new SimpleStringProperty("width: ")
.concat(stageRef.widthProperty().asString()));
textStageH.textProperty().bind(new SimpleStringProperty("height: ")
.concat(stageRef.heightProperty().asString()));
textStageF.textProperty().bind(new SimpleStringProperty("focused: ")
.concat(stageRef.focusedProperty().asString()));
//控制舞台是否可以改变大小
stage.setResizable(true);
checkBoxResizable.selectedProperty()
.bindBidirectional(stage.resizableProperty());
// 让舞台全屏
checkBoxFullScreen.selectedProperty().addListener((ov, oldValue, newValue) -> {
stageRef.setFullScreen(checkBoxFullScreen.selectedProperty().getValue());
});
title.bind(titleTextField.textProperty());
stage.setScene(scene);
stage.titleProperty().bind(title);
stage.initStyle(stageStyle);
//关闭舞台并检测何时关闭
stage.setOnCloseRequest((WindowEvent we) -> {
System.out.println("Stage is closing");
});
stage.show();
//使用用户界面布局容器
Rectangle2D primScreenBounds = Screen.getPrimary().getVisualBounds();
stage.setX((primScreenBounds.getWidth() - stage.getWidth()) / 2);
stage.setY((primScreenBounds.getHeight() - stage.getHeight()) / 4);
}
}
确定舞台是否处于焦点位置
textStageF.textProperty().bind(new SimpleStringProperty("focused: ")
.concat(stageRef.focusedProperty().asString()));
控制舞台的z轴顺序
Button toBackButton = new Button(“toBack()”);
toBackButton.setOnAction(e -> stageRef.toBack());
Button toFrontButton = new Button(“toFront()”);
toFrontButton.setOnAction(e -> stageRef.toFront());
package sample;
/**
* @author: Administrator
* @date: 2021/03/17 22:02
* @description:
*/
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.VPos;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Slider;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class OnTheSceneMain extends Application {
DoubleProperty fillVals = new SimpleDoubleProperty(255.0);
Scene sceneRef;
ObservableList cursors = FXCollections.observableArrayList(
Cursor.DEFAULT,
Cursor.CROSSHAIR,
Cursor.WAIT,
Cursor.TEXT,
Cursor.HAND,
Cursor.MOVE,
Cursor.N_RESIZE,
Cursor.NE_RESIZE,
Cursor.E_RESIZE,
Cursor.SE_RESIZE,
Cursor.S_RESIZE,
Cursor.SW_RESIZE,
Cursor.W_RESIZE,
Cursor.NW_RESIZE,
Cursor.NONE
);
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
Slider sliderRef;
ChoiceBox choiceBoxRef;
Text textSceneX;
Text textSceneY;
Text textSceneW;
Text textSceneH;
Label labelStageX;
Label labelStageY;
Label labelStageW;
Label labelStageH;
final ToggleGroup toggleGrp = new ToggleGroup();
sliderRef = new Slider(0, 255, 255);
sliderRef.setOrientation(Orientation.VERTICAL);
choiceBoxRef = new ChoiceBox(cursors);
HBox hbox = new HBox(sliderRef, choiceBoxRef);
hbox.setSpacing(10);
textSceneX = new Text();
textSceneX.getStyleClass().add("emphasized-text");
textSceneY = new Text();
textSceneY.getStyleClass().add("emphasized-text");
textSceneW = new Text();
textSceneW.getStyleClass().add("emphasized-text");
textSceneH = new Text();
textSceneH.getStyleClass().add("emphasized-text");
textSceneH.setId("sceneHeightText");
Hyperlink hyperlink = new Hyperlink("lookup");
hyperlink.setOnAction((javafx.event.ActionEvent e) -> {
System.out.println("sceneRef:" + sceneRef);
Text textRef = (Text) sceneRef.lookup("#sceneHeightText");
System.out.println(textRef.getText());
});
RadioButton radio1 = new RadioButton("onTheScene.css");
radio1.setSelected(true);
radio1.setToggleGroup(toggleGrp);
RadioButton radio2 = new RadioButton("changeOfScene.css");
radio2.setToggleGroup(toggleGrp);
labelStageX = new Label();
labelStageX.setId("stageX");
labelStageY = new Label();
labelStageY.setId("stageY");
labelStageW = new Label();
labelStageH = new Label();
FlowPane sceneRoot = new FlowPane(Orientation.VERTICAL, 20, 10, hbox,
textSceneX, textSceneY, textSceneW, textSceneH, hyperlink,
radio1, radio2,
labelStageX, labelStageY,
labelStageW,
labelStageH);
sceneRoot.setPadding(new Insets(0, 20, 40, 0));
sceneRoot.setColumnHalignment(HPos.LEFT);
sceneRoot.setLayoutX(20);
sceneRoot.setLayoutY(40);
sceneRef = new Scene(sceneRoot, 600, 250);
sceneRef.getStylesheets().add("onTheScene.css");
stage.setScene(sceneRef);
choiceBoxRef.getSelectionModel().selectFirst();
// Setup various property binding
textSceneX.textProperty().bind(new SimpleStringProperty("Scene x: ")
.concat(sceneRef.xProperty().asString()));
textSceneY.textProperty().bind(new SimpleStringProperty("Scene y: ")
.concat(sceneRef.yProperty().asString()));
textSceneW.textProperty().bind(new SimpleStringProperty("Scene width: ")
.concat(sceneRef.widthProperty().asString()));
textSceneH.textProperty().bind(new SimpleStringProperty("Scene height: ")
.concat(sceneRef.heightProperty().asString()));
labelStageX.textProperty().bind(new SimpleStringProperty("Stage x: ")
.concat(sceneRef.getWindow().xProperty().asString()));
labelStageY.textProperty().bind(new SimpleStringProperty("Stage y: ")
.concat(sceneRef.getWindow().yProperty().asString()));
labelStageW.textProperty().bind(new SimpleStringProperty("Stage width: ")
.concat(sceneRef.getWindow().widthProperty().asString()));
labelStageH.textProperty().bind(new SimpleStringProperty("Stage height: ")
.concat(sceneRef.getWindow().heightProperty().asString()));
sceneRef.cursorProperty().bind(choiceBoxRef.getSelectionModel()
.selectedItemProperty());
fillVals.bind(sliderRef.valueProperty());
// When fillVals changes, use that value as the RGB to fill the scene
fillVals.addListener((ov, oldValue, newValue) -> {
Double fillValue = fillVals.getValue() / 256.0;
sceneRef.setFill(new Color(fillValue, fillValue, fillValue, 1.0));
});
// When the selected radio button changes, set the appropriate style sheet
toggleGrp.selectedToggleProperty().addListener((ov, oldValue, newValue) -> {
String radioButtonText = ((RadioButton) toggleGrp.getSelectedToggle())
.getText();
sceneRef.getStylesheets().clear();
sceneRef.getStylesheets().addAll(radioButtonText);
});
stage.setTitle("On the Scene");
stage.show();
// Define an unmanaged node that will display Text
Text addedTextRef = new Text(0, -30, "");
addedTextRef.setTextOrigin(VPos.TOP);
addedTextRef.setFill(Color.BLUE);
addedTextRef.setFont(Font.font("Sans Serif", FontWeight.BOLD, 16));
addedTextRef.setManaged(false);
// Bind the text of the added Text node to the fill property of the Scene
addedTextRef.textProperty().bind(new SimpleStringProperty("Scene fill: ").
concat(sceneRef.fillProperty()));
// Add to the Text node to the FlowPane
((FlowPane) sceneRef.getRoot()).getChildren().add(addedTextRef);
}
}
设置场景中的光标
sceneRef.cursorProperty().bind(choiceBoxRef.getSelectionModel()
.selectedItemProperty());
通过ID找到场景中的节点
textSceneH = new Text();
textSceneH.getStyleClass().add("emphasized-text");
textSceneH.setId("sceneHeightText");
Hyperlink hyperlink = new Hyperlink("lookup");
hyperlink.setOnAction((javafx.event.ActionEvent e) -> {
System.out.println("sceneRef:" + sceneRef);
Text textRef = (Text) sceneRef.lookup("#sceneHeightText");
System.out.println(textRef.getText());
});
场景引用中根据ID获取文本对象,并获取对象的内容。
从场景访问舞台
labelStageX.textProperty().bind(new SimpleStringProperty("Stage x: ")
.concat(sceneRef.getWindow().xProperty().asString()));
labelStageY.textProperty().bind(new SimpleStringProperty("Stage y: ")
.concat(sceneRef.getWindow().yProperty().asString()));
向场景内容序列中插入节点
// Define an unmanaged node that will display Text
Text addedTextRef = new Text(0, -30, "");
addedTextRef.setTextOrigin(VPos.TOP);
addedTextRef.setFill(Color.BLUE);
addedTextRef.setFont(Font.font("Sans Serif", FontWeight.BOLD, 16));
addedTextRef.setManaged(false);
// Bind the text of the added Text node to the fill property of the Scene
addedTextRef.textProperty().bind(new SimpleStringProperty("Scene fill: ").
concat(sceneRef.fillProperty()));
// Add the Text node to the FlowPane
((FlowPane) sceneRef.getRoot()).getChildren().add(addedTextRef);
场景中用CSS来修饰节点
sceneRef.getStylesheets().add("onTheScene.css");
...code omitted...
// When the selected radio button changes, set the appropriate stylesheet
toggleGrp.selectedToggleProperty().addListener((ov, oldValue, newValue) -> {
String radioButtonText = ((RadioButton) toggleGrp.getSelectedToggle())
.getText();
sceneRef.getStylesheets().clear();
sceneRef.getStylesheets().addAll("/"+radioButtonText);
});
处理输入事件
- 查询鼠标、键盘、触摸和手势事件和处理程序
- 理解键盘事件
- 理解鼠标事件
- 理解触摸事件
- 理解手势事件
场景中的动画节点
package sample;
/**
* @author: Administrator
* @date: 2021/03/17 22:14
* @description:
*/
import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Metronome1Main extends Application {
DoubleProperty startXVal = new SimpleDoubleProperty(100.0);
Button startButton;
Button pauseButton;
Button resumeButton;
Button stopButton;
Line line;
Timeline anim;
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
anim = new Timeline(
new KeyFrame(new Duration(0.0), new KeyValue(startXVal, 100.)),
new KeyFrame(new Duration(1000.0), new KeyValue(startXVal, 300., Interpolator.LINEAR))
);
anim.setAutoReverse(true);
anim.setCycleCount(Animation.INDEFINITE);
line = new Line(0, 50, 200, 400);
line.setStrokeWidth(4);
line.setStroke(Color.BLUE);
startButton = new Button("start");
startButton.setOnAction(e -> anim.playFromStart());
pauseButton = new Button("pause");
pauseButton.setOnAction(e -> anim.pause());
resumeButton = new Button("resume");
resumeButton.setOnAction(e -> anim.play());
stopButton = new Button("stop");
stopButton.setOnAction(e -> anim.stop());
HBox commands = new HBox(10,
startButton,
pauseButton,
resumeButton,
stopButton);
commands.setLayoutX(60);
commands.setLayoutY(420);
Group group = new Group(line, commands);
Scene scene = new Scene(group, 400, 500);
line.startXProperty().bind(startXVal);
startButton.disableProperty().bind(anim.statusProperty()
.isNotEqualTo(Animation.Status.STOPPED));
pauseButton.disableProperty().bind(anim.statusProperty()
.isNotEqualTo(Animation.Status.RUNNING));
resumeButton.disableProperty().bind(anim.statusProperty()
.isNotEqualTo(Animation.Status.PAUSED));
stopButton.disableProperty().bind(anim.statusProperty()
.isEqualTo(Animation.Status.STOPPED));
stage.setScene(scene);
stage.setTitle("Metronome 1");
stage.show();
}
}
为动画使用时间线
DoubleProperty startXVal = new SimpleDoubleProperty(100.0);
...code omitted...
Timeline anim = new Timeline(
new KeyFrame(new Duration(0.0), new KeyValue(startXVal, 100.)),
new KeyFrame(new Duration(1000.0), new KeyValue(startXVal, 300., Interpolator.LINEAR))
);
anim.setAutoReverse(true);
anim.setCycleCount(Animation.INDEFINITE);
...code omitted...
line = new Line(0, 50, 200, 400);
line.setStrokeWidth(4);
line.setStroke(Color.BLUE);
...code omitted...
line.startXProperty().bind(startXVal);
line = LineBuilder.create()
.startY(50)
.endX(200)
.endY(400)
.strokeWidth(4)
.stroke(Color.BLUE)
.build();
为时间线插入KeyFrame
Timeline实例包含两个KeyValue实例,在1s时间内,startXVal从100线性变化到300。将这个属性绑定到line对象的startProperty属性上即可让直线运动起来。
Timeline anim = new Timeline(
new KeyFrame(new Duration(0.0), new KeyValue(startXVal, 100.)),
new KeyFrame(new Duration(1000.0), new KeyValue(startXVal, 300., Interpolator.LINEAR))
);
控制时间线
通过Timeline的playFromStart(),pause(),play(),stop()控制动画开始,暂停,播放,停止。
startButton = new Button("start");
startButton.setOnAction(e -> anim.playFromStart());
pauseButton = new Button("pause");
pauseButton.setOnAction(e -> anim.pause());
resumeButton = new Button("resume");
resumeButton.setOnAction(e -> anim.play());
stopButton = new Button("stop");
stopButton.setOnAction(e -> anim.stop());