大家好!今天我们来学习《Java 程序设计》中的第 15 章内容:事件处理与常用控件。这一章是 Java GUI 编程的核心,掌握这些内容将帮助你创建交互性强、用户体验好的桌面应用程序。
思维导图
15.1 事件处理
在 GUI 应用程序中,用户与界面的交互(如点击按钮、输入文本、移动鼠标等)都会产生事件。事件处理就是对这些用户行为做出响应的机制。
15.1.1 事件处理模型
Java 采用委托事件模型(Delegation Event Model) 来处理事件,其核心思想是:
- 事件源(Event Source):产生事件的组件(如按钮、文本框等)
- 事件对象(Event Object):封装了事件的相关信息
- 事件监听器(Event Listener):负责处理事件的对象
15.1.2 事件类和事件类型
JavaFX 中提供了多种事件类,用于表示不同类型的事件,常用的有:
ActionEvent
:动作事件,如按钮点击、菜单选择等MouseEvent
:鼠标事件,如点击、移动、拖拽等KeyEvent
:键盘事件,如按键按下、释放等WindowEvent
:窗口事件,如打开、关闭、最小化等ScrollEvent
:滚动事件TouchEvent
:触摸事件(适用于触摸屏设备)
下面是主要事件类的类图:
@startuml
class Event {
+getSource(): Object
+consume(): void
+isConsumed(): boolean
+getEventType(): EventType
}
class ActionEvent {
+getActionCommand(): String
+copyFor(Object, EventTarget): ActionEvent
}
class MouseEvent {
+getX(): double
+getY(): double
+getButton(): MouseButton
+getClickCount(): int
}
class KeyEvent {
+getCode(): KeyCode
+getText(): String
+isShiftDown(): boolean
+isControlDown(): boolean
}
class WindowEvent {
+getNewState(): int
+getOldState(): int
}
Event <|-- ActionEvent
Event <|-- MouseEvent
Event <|-- KeyEvent
Event <|-- WindowEvent
@enduml
15.1.3 使用事件处理器
在 JavaFX 中,我们通过事件监听器来处理事件。常用的注册监听器的方式有:
- 使用
setOnXXX()
方法(如setOnAction()
、setOnMouseClicked()
等) - 使用
addEventHandler()
方法 - 实现特定的事件监听器接口
下面是一个简单的示例,展示如何使用事件处理器:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class EventHandlerDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建按钮(事件源)
Button btn = new Button("点击我");
// 方式1:使用setOnAction()方法添加事件处理器(匿名内部类)
btn.setOnAction(new javafx.event.EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("按钮被点击了!");
}
});
// 方式2:使用Lambda表达式(Java 8+)
btn.setOnAction(e -> System.out.println("按钮被点击了(Lambda方式)!"));
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("事件处理器示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.1.4 动作事件
ActionEvent
是最常用的事件类型之一,通常由用户的交互动作触发,如点击按钮、在文本框中按 Enter 键、选择菜单项等。
下面是一个动作事件的综合示例:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ActionEventDemo extends Application {
private int clickCount = 0;
@Override
public void start(Stage primaryStage) {
// 创建控件
Label label = new Label("点击按钮或在文本框按Enter:");
Button btn = new Button("点击我");
TextField textField = new TextField();
Label resultLabel = new Label("结果将显示在这里");
// 按钮的动作事件处理器
btn.setOnAction(e -> {
clickCount++;
resultLabel.setText("按钮被点击了 " + clickCount + " 次");
});
// 文本框的动作事件处理器(按Enter键触发)
textField.setOnAction(e -> {
String text = textField.getText();
resultLabel.setText("你输入了:" + text);
textField.clear(); // 清空文本框
});
// 创建布局并添加控件
VBox root = new VBox(10); // 垂直布局,间距10
root.getChildren().addAll(label, btn, textField, resultLabel);
root.setStyle("-fx-padding: 20;"); // 设置内边距
// 创建场景并显示
Scene scene = new Scene(root, 300, 200);
primaryStage.setTitle("动作事件示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.1.5 鼠标事件
MouseEvent
处理与鼠标相关的事件,如点击、移动、拖拽、进入 / 离开组件等。
下面是一个鼠标事件的示例:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class MouseEventDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建控件
Label infoLabel = new Label("移动鼠标或点击圆形");
Label statusLabel = new Label("状态:等待鼠标操作");
// 创建一个圆形,作为鼠标事件的目标
Circle circle = new Circle(50, Color.BLUE);
// 鼠标进入圆形
circle.setOnMouseEntered(e -> {
statusLabel.setText("鼠标进入圆形");
circle.setFill(Color.GREEN);
});
// 鼠标离开圆形
circle.setOnMouseExited(e -> {
statusLabel.setText("鼠标离开圆形");
circle.setFill(Color.BLUE);
});
// 鼠标在圆形上移动
circle.setOnMouseMoved(e -> {
String pos = String.format("位置:(%.0f, %.0f)", e.getX(), e.getY());
statusLabel.setText(pos);
});
// 鼠标点击圆形
circle.setOnMouseClicked(e -> {
String msg = "";
if (e.getButton() == MouseButton.PRIMARY) {
msg += "左键点击";
} else if (e.getButton() == MouseButton.SECONDARY) {
msg += "右键点击";
}
if (e.getClickCount() == 2) {
msg += "(双击)";
}
statusLabel.setText(msg);
});
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(infoLabel, circle, statusLabel);
root.setStyle("-fx-padding: 20; -fx-alignment: center;");
// 创建场景并显示
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("鼠标事件示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.1.6 键盘事件
KeyEvent
用于处理键盘输入事件,包括按键按下、释放和敲击(按下并释放)等动作。
下面是一个键盘事件的示例:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class KeyEventDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建控件
Label label = new Label("请在下方区域输入内容,或按方向键:");
TextArea textArea = new TextArea();
textArea.setPrefRowCount(5);
Label statusLabel = new Label("状态:等待键盘输入");
// 按键按下事件
textArea.setOnKeyPressed(e -> {
KeyCode code = e.getCode();
String msg = "按下:" + code.getName();
// 检查是否按下了修饰键
if (e.isShiftDown()) msg += " + Shift";
if (e.isControlDown()) msg += " + Ctrl";
if (e.isAltDown()) msg += " + Alt";
statusLabel.setText(msg);
// 特殊处理方向键
if (code.isArrowKey()) {
e.consume(); // 消费事件,阻止文本区域处理方向键
statusLabel.setText("方向键 " + code.getName() + " 被按下");
}
});
// 按键释放事件
textArea.setOnKeyReleased(e -> {
statusLabel.setText("释放:" + e.getCode().getName());
});
// 按键敲击事件(按下并释放)
textArea.setOnKeyTyped(e -> {
String text = e.getCharacter();
if (!text.isEmpty()) {
statusLabel.setText("输入:" + text);
}
});
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(label, textArea, statusLabel);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 400, 300);
primaryStage.setTitle("键盘事件示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.1.7 为属性添加监听器
JavaFX 允许我们为控件的属性添加监听器,当属性值发生变化时得到通知。这在需要根据控件状态变化来更新 UI 或执行其他操作时非常有用。
下面是一个属性监听器的示例:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class PropertyListenerDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建控件
Label titleLabel = new Label("属性监听器示例");
titleLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 16px;");
// 文本框属性监听
Label textFieldLabel = new Label("文本框内容变化:");
TextField textField = new TextField();
Label textFieldStatus = new Label("当前内容:");
// 监听文本框的文本变化
textField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
textFieldStatus.setText("当前内容:" + newValue);
}
});
// 滑块属性监听
Label sliderLabel = new Label("滑块值变化:");
Slider slider = new Slider(0, 100, 50);
Label sliderStatus = new Label("当前值:" + slider.getValue());
// 监听滑块的值变化
slider.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
sliderStatus.setText("当前值:" + String.format("%.1f", newValue));
}
});
// 复选框属性监听
CheckBox checkBox = new CheckBox("选中我");
Label checkBoxStatus = new Label("选中状态:" + checkBox.isSelected());
// 监听复选框的选中状态变化
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
checkBoxStatus.setText("选中状态:" + newValue);
}
});
// 创建布局并添加控件
VBox root = new VBox(15);
root.getChildren().addAll(
titleLabel,
textFieldLabel, textField, textFieldStatus,
sliderLabel, slider, sliderStatus,
checkBox, checkBoxStatus
);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 300, 350);
primaryStage.setTitle("属性监听器示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2 常用控件
JavaFX 提供了丰富的 UI 控件,用于构建用户界面。下面我们介绍一些最常用的控件及其用法。
15.2.1 Label 类
Label
是用于显示文本的控件,它不能被用户直接编辑。可以设置文本、字体、颜色等属性。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class LabelDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建不同样式的Label
Label label1 = new Label("普通标签");
Label label2 = new Label("带有样式的标签");
label2.setFont(new Font("Arial", 16)); // 设置字体和大小
label2.setTextFill(Color.BLUE); // 设置文本颜色
Label label3 = new Label("多行文本标签\n第二行文本\n第三行文本");
label3.setStyle("-fx-font-weight: bold; -fx-text-fill: green;");
// 创建带图标的标签(需要有对应的图片文件)
Label iconLabel = new Label("带图标的标签");
// 如果有图片,可以这样设置:
// Image image = new Image("icon.png");
// iconLabel.setGraphic(new ImageView(image));
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(label1, label2, label3, iconLabel);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Label示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2.2 Button 类
Button
是用于触发动作的控件,用户可以点击它来执行特定操作。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ButtonDemo extends Application {
private int clickCount = 0;
@Override
public void start(Stage primaryStage) {
// 创建标签显示信息
Label label = new Label("点击按钮进行操作");
Label statusLabel = new Label("按钮未被点击");
// 创建普通按钮
Button clickButton = new Button("点击我");
clickButton.setOnAction(e -> {
clickCount++;
statusLabel.setText("按钮被点击了 " + clickCount + " 次");
});
// 创建禁用按钮
Button disabledButton = new Button("我是禁用的");
disabledButton.setDisable(true); // 禁用按钮
// 创建带图标的按钮(需要有对应的图片文件)
Button iconButton = new Button("带图标的按钮");
// 如果有图片,可以这样设置:
// Image image = new Image("button-icon.png");
// iconButton.setGraphic(new ImageView(image));
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(label, clickButton, disabledButton, iconButton, statusLabel);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Button示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2.3 TextField 类和 PasswordField 类
TextField
用于接收用户的单行文本输入,PasswordField
是TextField
的子类,专门用于输入密码,输入的内容会被掩盖。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class TextFieldDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建标签和输入框
Label nameLabel = new Label("用户名:");
TextField nameField = new TextField();
nameField.setPromptText("请输入用户名"); // 设置提示文本
Label passwordLabel = new Label("密码:");
PasswordField passwordField = new PasswordField();
passwordField.setPromptText("请输入密码");
Label infoLabel = new Label();
// 创建按钮
Button submitButton = new Button("提交");
submitButton.setOnAction(e -> {
String username = nameField.getText();
String password = passwordField.getText();
infoLabel.setText("用户名:" + username + ",密码长度:" + password.length());
});
Button clearButton = new Button("清空");
clearButton.setOnAction(e -> {
nameField.clear();
passwordField.clear();
infoLabel.setText("");
});
// 创建网格布局并添加控件
GridPane root = new GridPane();
root.setHgap(10); // 水平间距
root.setVgap(10); // 垂直间距
root.setStyle("-fx-padding: 20;");
root.add(nameLabel, 0, 0);
root.add(nameField, 1, 0);
root.add(passwordLabel, 0, 1);
root.add(passwordField, 1, 1);
root.add(submitButton, 0, 2);
root.add(clearButton, 1, 2);
root.add(infoLabel, 0, 3, 2, 1); // 跨两列
// 创建场景并显示
Scene scene = new Scene(root, 350, 200);
primaryStage.setTitle("TextField和PasswordField示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2.4 TextArea 类
TextArea
用于接收或显示多行文本,适合需要输入或显示大量文本的场景。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextAreaDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建标签
Label label = new Label("请输入多行文本:");
// 创建文本区域
TextArea textArea = new TextArea();
textArea.setPromptText("在这里输入多行文本...");
textArea.setPrefRowCount(10); // 设置默认行数
textArea.setWrapText(true); // 自动换行
// 创建按钮
Button countButton = new Button("统计字数");
Button clearButton = new Button("清空内容");
Label statusLabel = new Label();
// 按钮事件处理
countButton.setOnAction(e -> {
String text = textArea.getText();
int charCount = text.length();
int lineCount = textArea.getParagraphs().size();
statusLabel.setText("字符数:" + charCount + ",行数:" + lineCount);
});
clearButton.setOnAction(e -> {
textArea.clear();
statusLabel.setText("");
});
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(label, textArea, countButton, clearButton, statusLabel);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 400, 350);
primaryStage.setTitle("TextArea示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2.5 CheckBox 类
CheckBox
是复选框控件,允许用户选择或取消选择一个选项,多个复选框可以同时被选中。
import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CheckBoxDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建标签
Label label = new Label("请选择你喜欢的编程语言:");
Label selectedLabel = new Label("你选择了:");
// 创建复选框
CheckBox javaBox = new CheckBox("Java");
CheckBox pythonBox = new CheckBox("Python");
CheckBox cBox = new CheckBox("C++");
CheckBox jsBox = new CheckBox("JavaScript");
// 设置一个默认选中
javaBox.setSelected(true);
// 监听每个复选框的选中状态变化
javaBox.selectedProperty().addListener((obs, oldVal, newVal) -> updateSelectedLabel(selectedLabel, javaBox, pythonBox, cBox, jsBox));
pythonBox.selectedProperty().addListener((obs, oldVal, newVal) -> updateSelectedLabel(selectedLabel, javaBox, pythonBox, cBox, jsBox));
cBox.selectedProperty().addListener((obs, oldVal, newVal) -> updateSelectedLabel(selectedLabel, javaBox, pythonBox, cBox, jsBox));
jsBox.selectedProperty().addListener((obs, oldVal, newVal) -> updateSelectedLabel(selectedLabel, javaBox, pythonBox, cBox, jsBox));
// 初始更新一次标签
updateSelectedLabel(selectedLabel, javaBox, pythonBox, cBox, jsBox);
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(label, javaBox, pythonBox, cBox, jsBox, selectedLabel);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("CheckBox示例");
primaryStage.setScene(scene);
primaryStage.show();
}
// 更新选中状态标签
private void updateSelectedLabel(Label label, CheckBox... checkBoxes) {
StringBuilder sb = new StringBuilder("你选择了:");
boolean first = true;
for (CheckBox cb : checkBoxes) {
if (cb.isSelected()) {
if (!first) {
sb.append("、");
}
sb.append(cb.getText());
first = false;
}
}
if (first) {
sb.append("没有选择任何语言");
}
label.setText(sb.toString());
}
public static void main(String[] args) {
launch(args);
}
}
15.2.6 RadioButton 类
RadioButton
是单选按钮控件,通常成组使用,在一组单选按钮中只能有一个被选中。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class RadioButtonDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建标签
Label label = new Label("请选择你的性别:");
Label selectedLabel = new Label("你的选择:");
// 创建单选按钮组
ToggleGroup group = new ToggleGroup();
// 创建单选按钮并加入到组中
RadioButton maleButton = new RadioButton("男");
maleButton.setToggleGroup(group);
RadioButton femaleButton = new RadioButton("女");
femaleButton.setToggleGroup(group);
RadioButton otherButton = new RadioButton("其他");
otherButton.setToggleGroup(group);
// 设置一个默认选中
maleButton.setSelected(true);
// 监听选择变化
group.selectedToggleProperty().addListener((obs, oldVal, newVal) -> {
if (newVal != null) {
RadioButton selected = (RadioButton) newVal;
selectedLabel.setText("你的选择:" + selected.getText());
}
});
// 初始更新一次标签
selectedLabel.setText("你的选择:" + ((RadioButton) group.getSelectedToggle()).getText());
// 创建第二个单选按钮组 - 选择喜欢的颜色
Label colorLabel = new Label("\n请选择你喜欢的颜色:");
ToggleGroup colorGroup = new ToggleGroup();
RadioButton redButton = new RadioButton("红色");
redButton.setToggleGroup(colorGroup);
RadioButton greenButton = new RadioButton("绿色");
greenButton.setToggleGroup(colorGroup);
RadioButton blueButton = new RadioButton("蓝色");
blueButton.setToggleGroup(colorGroup);
// 监听颜色选择变化
colorGroup.selectedToggleProperty().addListener((obs, oldVal, newVal) -> {
if (newVal != null) {
// 可以根据选择的颜色改变界面样式
String color = ((RadioButton) newVal).getText();
String style = "";
switch (color) {
case "红色":
style = "-fx-text-fill: red;";
break;
case "绿色":
style = "-fx-text-fill: green;";
break;
case "蓝色":
style = "-fx-text-fill: blue;";
break;
}
colorLabel.setStyle(style);
}
});
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(
label, maleButton, femaleButton, otherButton, selectedLabel,
colorLabel, redButton, greenButton, blueButton
);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 300, 350);
primaryStage.setTitle("RadioButton示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2.7 ComboBox 类
ComboBox
是下拉列表控件,允许用户从预定义的选项中选择一个。
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ComboBoxDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建标签
Label label = new Label("请选择你的专业:");
Label selectedLabel = new Label("你选择了:");
// 创建选项列表
ObservableList<String> majors = FXCollections.observableArrayList(
"计算机科学", "软件工程", "电子信息工程",
"机械工程", "土木工程", "经济学", "管理学"
);
// 创建下拉列表
ComboBox<String> comboBox = new ComboBox<>(majors);
comboBox.setPromptText("请选择...");
comboBox.setEditable(true); // 设置为可编辑,允许用户输入新值
// 监听选择变化
comboBox.getSelectionModel().selectedItemProperty().addListener(
(obs, oldVal, newVal) -> {
if (newVal != null) {
selectedLabel.setText("你选择了:" + newVal);
}
}
);
// 创建第二个下拉列表 - 选择城市
Label cityLabel = new Label("\n请选择你所在的城市:");
ComboBox<String> cityComboBox = new ComboBox<>();
cityComboBox.setPromptText("请选择城市...");
// 动态添加选项
cityComboBox.getItems().addAll("北京", "上海", "广州", "深圳", "杭州");
// 添加一个按钮来添加新城市
Label dynamicLabel = new Label("输入新城市并按回车添加:");
ComboBox<String> dynamicComboBox = new ComboBox<>();
dynamicComboBox.setEditable(true);
dynamicComboBox.setPromptText("输入新城市...");
// 回车时添加新选项
dynamicComboBox.setOnAction(e -> {
String newCity = dynamicComboBox.getEditor().getText();
if (newCity != null && !newCity.isEmpty() &&
!dynamicComboBox.getItems().contains(newCity)) {
dynamicComboBox.getItems().add(newCity);
dynamicComboBox.setValue(newCity);
}
});
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(
label, comboBox, selectedLabel,
cityLabel, cityComboBox,
dynamicLabel, dynamicComboBox
);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 300, 400);
primaryStage.setTitle("ComboBox示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2.8 Slider 类
Slider
是滑块控件,允许用户通过拖动滑块来选择一个范围内的值。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class SliderDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建标签
Label titleLabel = new Label("滑块示例");
titleLabel.setFont(new Font(16));
// 1. 音量滑块(0-100)
Label volumeLabel = new Label("音量控制:");
Slider volumeSlider = new Slider(0, 100, 50); // 最小值,最大值,初始值
volumeSlider.setShowTickLabels(true); // 显示刻度标签
volumeSlider.setShowTickMarks(true); // 显示刻度线
volumeSlider.setMajorTickUnit(20); // 主刻度间隔
volumeSlider.setMinorTickCount(4); // 主刻度之间的小刻度数
Label volumeValueLabel = new Label("当前音量:50%");
// 监听音量滑块变化
volumeSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
int value = (int) Math.round(newVal.doubleValue());
volumeValueLabel.setText("当前音量:" + value + "%");
});
// 2. 亮度滑块(0-1)
Label brightnessLabel = new Label("\n亮度控制:");
Slider brightnessSlider = new Slider(0, 1, 0.7);
brightnessSlider.setOrientation(javafx.geometry.Orientation.HORIZONTAL);
Label brightnessValueLabel = new Label("当前亮度:70%");
// 监听亮度滑块变化
brightnessSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
double value = newVal.doubleValue();
brightnessValueLabel.setText(String.format("当前亮度:%.0f%%", value * 100));
// 改变背景亮度
root.setStyle(String.format("-fx-background-color: rgba(255,255,255,%.2f); -fx-padding: 20;", value));
});
// 3. 字体大小滑块(10-30)
Label fontSizeLabel = new Label("\n字体大小控制:");
Slider fontSizeSlider = new Slider(10, 30, 16);
Label fontSizeValueLabel = new Label("当前字体大小:16px");
Label sampleTextLabel = new Label("这是一段示例文本,用于展示字体大小变化");
// 监听字体大小滑块变化
fontSizeSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
int value = (int) Math.round(newVal.doubleValue());
fontSizeValueLabel.setText("当前字体大小:" + value + "px");
sampleTextLabel.setFont(new Font(value));
});
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(
titleLabel,
volumeLabel, volumeSlider, volumeValueLabel,
brightnessLabel, brightnessSlider, brightnessValueLabel,
fontSizeLabel, fontSizeSlider, fontSizeValueLabel, sampleTextLabel
);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 400, 450);
primaryStage.setTitle("Slider示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2.9 菜单设计
JavaFX 提供了完整的菜单组件,包括MenuBar
(菜单栏)、Menu
(菜单)、MenuItem
(菜单项)等,可以创建复杂的应用程序菜单。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MenuDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建主面板
BorderPane root = new BorderPane();
// 创建文本区域作为主内容
TextArea textArea = new TextArea();
textArea.setPromptText("这是一个文本编辑区域...");
root.setCenter(textArea);
// 创建状态栏
Label statusLabel = new Label("就绪");
root.setBottom(statusLabel);
// 创建菜单栏
MenuBar menuBar = new MenuBar();
// 1. 文件菜单
Menu fileMenu = new Menu("文件(F)");
fileMenu.setMnemonicParsing(true); // 启用助记符解析(Alt+F)
// 文件菜单项
MenuItem newItem = new MenuItem("新建(N)");
newItem.setMnemonicParsing(true);
newItem.setOnAction(e -> {
textArea.clear();
statusLabel.setText("新建文件");
});
MenuItem openItem = new MenuItem("打开(O)");
openItem.setMnemonicParsing(true);
openItem.setOnAction(e -> statusLabel.setText("打开文件"));
MenuItem saveItem = new MenuItem("保存(S)");
saveItem.setMnemonicParsing(true);
saveItem.setOnAction(e -> statusLabel.setText("保存文件"));
// 添加分隔线
SeparatorMenuItem separator = new SeparatorMenuItem();
MenuItem exitItem = new MenuItem("退出(X)");
exitItem.setMnemonicParsing(true);
exitItem.setOnAction(e -> primaryStage.close());
// 将菜单项添加到文件菜单
fileMenu.getItems().addAll(newItem, openItem, saveItem, separator, exitItem);
// 2. 编辑菜单
Menu editMenu = new Menu("编辑(E)");
editMenu.setMnemonicParsing(true);
MenuItem cutItem = new MenuItem("剪切(T)");
cutItem.setMnemonicParsing(true);
cutItem.setOnAction(e -> {
textArea.cut();
statusLabel.setText("剪切");
});
MenuItem copyItem = new MenuItem("复制(C)");
copyItem.setMnemonicParsing(true);
copyItem.setOnAction(e -> {
textArea.copy();
statusLabel.setText("复制");
});
MenuItem pasteItem = new MenuItem("粘贴(P)");
pasteItem.setMnemonicParsing(true);
pasteItem.setOnAction(e -> {
textArea.paste();
statusLabel.setText("粘贴");
});
// 添加子菜单:格式
Menu formatMenu = new Menu("格式(O)");
formatMenu.setMnemonicParsing(true);
// 子菜单项:字体大小
Menu fontSizeMenu = new Menu("字体大小");
RadioMenuItem smallItem = new RadioMenuItem("小");
RadioMenuItem mediumItem = new RadioMenuItem("中");
RadioMenuItem largeItem = new RadioMenuItem("大");
mediumItem.setSelected(true);
// 将单选菜单项加入到一个组中
ToggleGroup sizeGroup = new ToggleGroup();
smallItem.setToggleGroup(sizeGroup);
mediumItem.setToggleGroup(sizeGroup);
largeItem.setToggleGroup(sizeGroup);
// 监听字体大小变化
sizeGroup.selectedToggleProperty().addListener((obs, oldVal, newVal) -> {
if (newVal == smallItem) {
textArea.setFont(new javafx.scene.text.Font(12));
statusLabel.setText("字体大小:小");
} else if (newVal == mediumItem) {
textArea.setFont(new javafx.scene.text.Font(16));
statusLabel.setText("字体大小:中");
} else if (newVal == largeItem) {
textArea.setFont(new javafx.scene.text.Font(20));
statusLabel.setText("字体大小:大");
}
});
fontSizeMenu.getItems().addAll(smallItem, mediumItem, largeItem);
// 子菜单项:粗体
CheckMenuItem boldItem = new CheckMenuItem("粗体(B)");
boldItem.setMnemonicParsing(true);
boldItem.setOnAction(e -> statusLabel.setText("粗体:" + boldItem.isSelected()));
formatMenu.getItems().addAll(fontSizeMenu, boldItem);
editMenu.getItems().addAll(cutItem, copyItem, pasteItem, separator, formatMenu);
// 3. 帮助菜单
Menu helpMenu = new Menu("帮助(H)");
helpMenu.setMnemonicParsing(true);
MenuItem aboutItem = new MenuItem("关于(A)");
aboutItem.setMnemonicParsing(true);
aboutItem.setOnAction(e -> {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("关于");
alert.setHeaderText("文本编辑器");
alert.setContentText("这是一个简单的文本编辑器示例,用于演示JavaFX菜单功能。");
alert.showAndWait();
});
helpMenu.getItems().add(aboutItem);
// 将所有菜单添加到菜单栏
menuBar.getMenus().addAll(fileMenu, editMenu, helpMenu);
// 将菜单栏添加到主面板顶部
root.setTop(menuBar);
// 创建场景并显示
Scene scene = new Scene(root, 600, 400);
primaryStage.setTitle("菜单示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.2.10 FileChooser 类
FileChooser
是文件选择对话框,用于让用户选择文件或目录,常用于打开文件、保存文件等操作。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
public class FileChooserDemo extends Application {
private Stage primaryStage;
private TextArea textArea;
private String currentFilePath = null; // 当前打开的文件路径
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
// 创建文本区域
textArea = new TextArea();
textArea.setPrefRowCount(15);
// 创建按钮
Button openButton = new Button("打开文件");
openButton.setOnAction(e -> openFile());
Button saveButton = new Button("保存文件");
saveButton.setOnAction(e -> saveFile());
Button saveAsButton = new Button("另存为...");
saveAsButton.setOnAction(e -> saveFileAs());
// 创建状态标签
Label statusLabel = new Label("就绪");
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(
new Label("简易文本编辑器"),
new HBox(10, openButton, saveButton, saveAsButton),
textArea,
statusLabel
);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 600, 500);
primaryStage.setTitle("FileChooser示例");
primaryStage.setScene(scene);
primaryStage.show();
}
// 打开文件
private void openFile() {
FileChooser fileChooser = new FileChooser();
// 设置标题
fileChooser.setTitle("打开文本文件");
// 设置初始目录
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
// 设置文件过滤器
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("文本文件", "*.txt"),
new FileChooser.ExtensionFilter("Java文件", "*.java"),
new FileChooser.ExtensionFilter("所有文件", "*.*")
);
// 显示打开文件对话框
File file = fileChooser.showOpenDialog(primaryStage);
if (file != null) {
try {
// 读取文件内容
byte[] bytes = Files.readAllBytes(Paths.get(file.getPath()));
String content = new String(bytes, StandardCharsets.UTF_8);
textArea.setText(content);
currentFilePath = file.getPath();
primaryStage.setTitle("FileChooser示例 - " + file.getName());
} catch (IOException e) {
showAlert("错误", "无法打开文件", e.getMessage());
}
}
}
// 保存文件
private void saveFile() {
if (currentFilePath != null) {
saveToFile(new File(currentFilePath));
} else {
saveFileAs();
}
}
// 另存为
private void saveFileAs() {
FileChooser fileChooser = new FileChooser();
// 设置标题
fileChooser.setTitle("保存文本文件");
// 设置初始目录
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
// 设置建议的文件名
fileChooser.setInitialFileName("untitled.txt");
// 设置文件过滤器
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("文本文件", "*.txt"));
// 显示保存文件对话框
File file = fileChooser.showSaveDialog(primaryStage);
if (file != null) {
// 如果文件没有.txt扩展名,则添加
if (!file.getPath().endsWith(".txt")) {
file = new File(file.getPath() + ".txt");
}
saveToFile(file);
}
}
// 保存内容到文件
private void saveToFile(File file) {
try {
// 写入文件内容
Files.write(Paths.get(file.getPath()),
textArea.getText().getBytes(StandardCharsets.UTF_8));
currentFilePath = file.getPath();
primaryStage.setTitle("FileChooser示例 - " + file.getName());
showAlert("成功", "文件保存成功", "文件已保存到:" + file.getPath());
} catch (IOException e) {
showAlert("错误", "无法保存文件", e.getMessage());
}
}
// 显示对话框
private void showAlert(String title, String header, String content) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle(title);
alert.setHeaderText(header);
alert.setContentText(content);
alert.showAndWait();
}
public static void main(String[] args) {
launch(args);
}
}
15.3 音频和视频
JavaFX 提供了对音频和视频的支持,可以轻松地在应用程序中播放音频和视频文件。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
public class MediaDemo extends Application {
private MediaPlayer audioPlayer;
private MediaPlayer videoPlayer;
@Override
public void start(Stage primaryStage) {
// 创建音频控制区域
Label audioLabel = new Label("音频播放:");
Button audioOpenButton = new Button("选择音频文件");
Button audioPlayButton = new Button("播放");
Button audioPauseButton = new Button("暂停");
Button audioStopButton = new Button("停止");
// 禁用音频控制按钮,直到选择了文件
audioPlayButton.setDisable(true);
audioPauseButton.setDisable(true);
audioStopButton.setDisable(true);
// 音频按钮事件
audioOpenButton.setOnAction(e -> {
// 停止当前播放的音频
if (audioPlayer != null) {
audioPlayer.stop();
}
// 选择音频文件
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("选择音频文件");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("音频文件", "*.mp3", "*.wav", "*.aac")
);
File file = fileChooser.showOpenDialog(primaryStage);
if (file != null) {
try {
Media media = new Media(file.toURI().toString());
audioPlayer = new MediaPlayer(media);
// 启用控制按钮
audioPlayButton.setDisable(false);
audioPauseButton.setDisable(false);
audioStopButton.setDisable(false);
audioLabel.setText("正在准备播放:" + file.getName());
} catch (Exception ex) {
audioLabel.setText("无法打开音频文件:" + ex.getMessage());
}
}
});
audioPlayButton.setOnAction(e -> {
if (audioPlayer != null) {
audioPlayer.play();
audioLabel.setText("正在播放音频...");
}
});
audioPauseButton.setOnAction(e -> {
if (audioPlayer != null) {
audioPlayer.pause();
audioLabel.setText("音频已暂停");
}
});
audioStopButton.setOnAction(e -> {
if (audioPlayer != null) {
audioPlayer.stop();
audioLabel.setText("音频已停止");
}
});
// 创建视频控制区域
Label videoLabel = new Label("\n视频播放:");
Button videoOpenButton = new Button("选择视频文件");
Button videoPlayButton = new Button("播放");
Button videoPauseButton = new Button("暂停");
Button videoStopButton = new Button("停止");
// 禁用视频控制按钮,直到选择了文件
videoPlayButton.setDisable(true);
videoPauseButton.setDisable(true);
videoStopButton.setDisable(true);
// 创建视频视图
MediaView mediaView = new MediaView();
mediaView.setFitWidth(400);
mediaView.setFitHeight(300);
mediaView.setPreserveRatio(true);
// 视频按钮事件
videoOpenButton.setOnAction(e -> {
// 停止当前播放的视频
if (videoPlayer != null) {
videoPlayer.stop();
}
// 选择视频文件
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("选择视频文件");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("视频文件", "*.mp4", "*.mov", "*.avi")
);
File file = fileChooser.showOpenDialog(primaryStage);
if (file != null) {
try {
Media media = new Media(file.toURI().toString());
videoPlayer = new MediaPlayer(media);
mediaView.setMediaPlayer(videoPlayer);
// 视频结束时的事件
videoPlayer.setOnEndOfMedia(() -> {
videoLabel.setText("视频播放结束");
});
// 启用控制按钮
videoPlayButton.setDisable(false);
videoPauseButton.setDisable(false);
videoStopButton.setDisable(false);
videoLabel.setText("正在准备播放:" + file.getName());
} catch (Exception ex) {
videoLabel.setText("无法打开视频文件:" + ex.getMessage());
}
}
});
videoPlayButton.setOnAction(e -> {
if (videoPlayer != null) {
videoPlayer.play();
videoLabel.setText("正在播放视频...");
}
});
videoPauseButton.setOnAction(e -> {
if (videoPlayer != null) {
videoPlayer.pause();
videoLabel.setText("视频已暂停");
}
});
videoStopButton.setOnAction(e -> {
if (videoPlayer != null) {
videoPlayer.stop();
videoLabel.setText("视频已停止");
}
});
// 创建布局并添加控件
VBox root = new VBox(10);
root.getChildren().addAll(
new Label("音频和视频播放示例"),
// 音频部分
audioLabel,
new HBox(10, audioOpenButton, audioPlayButton, audioPauseButton, audioStopButton),
// 视频部分
videoLabel,
new HBox(10, videoOpenButton, videoPlayButton, videoPauseButton, videoStopButton),
mediaView
);
root.setStyle("-fx-padding: 20;");
// 创建场景并显示
Scene scene = new Scene(root, 500, 500);
primaryStage.setTitle("音频和视频示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.4 动画
JavaFX 提供了强大的动画支持,可以创建各种动画效果,如过渡、淡入淡出、移动、缩放、旋转等。
15.4.1 过渡动画
过渡动画是指在一段时间内,将控件的属性从一个值平滑地过渡到另一个值。
import javafx.animation.FadeTransition;
import javafx.animation.ScaleTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TransitionAnimationDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建一个矩形作为动画对象
Rectangle rect = new Rectangle(100, 100, Color.BLUE);
// 创建按钮
Button fadeButton = new Button("淡入淡出");
Button moveButton = new Button("移动");
Button scaleButton = new Button("缩放");
// 淡入淡出动画
fadeButton.setOnAction(e -> {
FadeTransition fade = new FadeTransition(Duration.seconds(2), rect);
fade.setFromValue(1.0); // 开始时完全不透明
fade.setToValue(0.1); // 结束时几乎透明
fade.setCycleCount(2); // 循环2次
fade.setAutoReverse(true); // 自动反转
fade.play();
});
// 移动动画
moveButton.setOnAction(e -> {
TranslateTransition translate = new TranslateTransition(Duration.seconds(2), rect);
translate.setFromX(0); // 开始X坐标
translate.setToX(200); // 结束X坐标
translate.setCycleCount(2);
translate.setAutoReverse(true);
translate.play();
});
// 缩放动画
scaleButton.setOnAction(e -> {
ScaleTransition scale = new ScaleTransition(Duration.seconds(2), rect);
scale.setFromX(1); // 开始X方向缩放比例
scale.setFromY(1); // 开始Y方向缩放比例
scale.setToX(1.5); // 结束X方向缩放比例
scale.setToY(1.5); // 结束Y方向缩放比例
scale.setCycleCount(2);
scale.setAutoReverse(true);
scale.play();
});
// 创建布局并添加控件
VBox root = new VBox(20);
HBox buttons = new HBox(10, fadeButton, moveButton, scaleButton);
root.getChildren().addAll(buttons, rect);
root.setStyle("-fx-padding: 20; -fx-alignment: center;");
// 创建场景并显示
Scene scene = new Scene(root, 400, 300);
primaryStage.setTitle("过渡动画示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.4.2 淡出效果
淡出效果是一种常见的动画效果,使控件逐渐变得透明。
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FadeOutDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建一个图片视图(如果有图片的话)
ImageView imageView = new ImageView();
// 尝试加载Java图标(如果没有图片,会显示空白)
try {
Image image = new Image("https://upload.wikimedia.org/wikipedia/en/thumb/3/30/Java_programming_language_logo.svg/1200px-Java_programming_language_logo.svg.png");
imageView.setImage(image);
imageView.setFitWidth(200);
imageView.setFitHeight(200);
imageView.setPreserveRatio(true);
} catch (Exception e) {
// 如果图片加载失败,使用一个矩形替代
javafx.scene.shape.Rectangle rect = new Rectangle(200, 200, javafx.scene.paint.Color.RED);
imageView = new ImageView(); // 清空图片视图
// 这里只是为了演示,实际中可以直接使用矩形
}
// 创建按钮
Button fadeOutButton = new Button("淡出效果");
Button fadeInButton = new Button("淡入效果");
Button fadeToggleButton = new Button("淡入淡出切换");
// 淡出效果
fadeOutButton.setOnAction(e -> {
FadeTransition fade = new FadeTransition(Duration.seconds(2), imageView);
fade.setFromValue(1.0);
fade.setToValue(0.0);
fade.play();
});
// 淡入效果
fadeInButton.setOnAction(e -> {
FadeTransition fade = new FadeTransition(Duration.seconds(2), imageView);
fade.setFromValue(imageView.getOpacity());
fade.setToValue(1.0);
fade.play();
});
// 淡入淡出切换
fadeToggleButton.setOnAction(e -> {
FadeTransition fade = new FadeTransition(Duration.seconds(2), imageView);
if (imageView.getOpacity() > 0.5) {
fade.setFromValue(1.0);
fade.setToValue(0.0);
} else {
fade.setFromValue(0.0);
fade.setToValue(1.0);
}
fade.play();
});
// 创建布局并添加控件
VBox root = new VBox(20);
root.getChildren().addAll(fadeOutButton, fadeInButton, fadeToggleButton, imageView);
root.setStyle("-fx-padding: 20; -fx-alignment: center;");
// 创建场景并显示
Scene scene = new Scene(root, 300, 350);
primaryStage.setTitle("淡出效果示例");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15.4.3 移动效果
移动效果使控件在界面上按照指定的路径移动。
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import javafx.util.Duration;
public class MoveEffectDemo extends Application {
@Override
public void start(Stage primaryStage) {
// 创建一个面板作为容器
Pane pane = new Pane();
pane.setPrefSize(400, 300);
pane.setStyle("-fx-background-color: #f0f0f0;");
// 创建一个圆形作为移动的对象
Circle circle = new Circle(20, Color.RED);
// 添加圆形到面板
pane.getChildren().add(circle);
// 创建按钮
Button linearMoveButton = new Button("直线移动");
Button pathMoveButton = new Button("沿路径移动");
Button resetButton = new Button("重置位置");
// 直线移动
linearMoveButton.setOnAction(e -> {
// 停止任何正在进行的动画
stopAllAnimations(circle);
// 创建平移过渡
javafx.animation.TranslateTransition translate =
new javafx.animation.TranslateTransition(Duration.seconds(3), circle);
translate.setFromX(0);
translate.setFromY(0);
translate.setToX(300);
translate.setToY(200);
translate.play();
});
// 沿路径移动
pathMoveButton.setOnAction(e -> {
// 停止任何正在进行的动画
stopAllAnimations(circle);
// 重置位置
circle.setTranslateX(0);
circle.setTranslateY(0);
circle.setX(20);
circle.setY(20);
// 创建一个路径(三角形)
Shape path = new javafx.scene.shape.Path(
new javafx.scene.shape.MoveTo(20, 20),
new javafx.scene.shape.LineTo(300, 20),
new javafx.scene.shape.LineTo(160, 200),
new javafx.scene.shape.ClosePath()
);
path.setStroke(Color.LIGHTGRAY);
path.setFill(null);
pane.getChildren().add(path); // 添加路径到面板,仅用于可视化
// 创建路径过渡
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(5));
pathTransition.setPath(path);
pathTransition.setNode(circle);
pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransition.setCycleCount(2);
pathTransition.setAutoReverse(false);
// 动画结束后移除路径
pathTransition.setOnFinished(event -> pane.getChildren().remove(path));
pathTransition.play();
});
// 重置位置
resetButton.setOnAction(e -> {
stopAllAnimations(circle);
circle.setTranslateX(0);
circle.setTranslateY(0);
circle.setX(20);
circle.setY(20);
});
// 创建布局并添加控件
HBox buttons = new HBox(10, linearMoveButton, pathMoveButton, resetButton);
VBox root = new VBox(10, buttons, pane);
root.setStyle("-fx-padding: 10;");
// 创建场景并显示
Scene scene = new Scene(root, 420, 350);
primaryStage.setTitle("移动效果示例");
primaryStage.setScene(scene);
primaryStage.show();
}
// 停止节点上的所有动画
private void stopAllAnimations(javafx.scene.Node node) {
for (javafx.animation.Animation animation : javafx.animation.Animation.getAnimations()) {
if (animation.getTargetNodes().contains(node)) {
animation.stop();
}
}
}
public static void main(String[] args) {
launch(args);
}
}
通过本章学习,可掌握 Java 桌面应用的核心交互能力,从简单按钮响应到复杂动画效果,为构建功能完整、体验流畅的 GUI 程序奠定基础。后续可结合布局管理器(如GridPane
、BorderPane
)和样式表(CSS)进一步优化界面设计。