当前位置: 首页 > news >正文

《Java 程序设计》第 15 章 - 事件处理与常用控件

        大家好!今天我们来学习《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 中,我们通过事件监听器来处理事件。常用的注册监听器的方式有:

  1. 使用setOnXXX()方法(如setOnAction()setOnMouseClicked()等)
  2. 使用addEventHandler()方法
  3. 实现特定的事件监听器接口

下面是一个简单的示例,展示如何使用事件处理器:

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 {@Overridepublic void start(Stage primaryStage) {// 创建按钮(事件源)Button btn = new Button("点击我");// 方式1:使用setOnAction()方法添加事件处理器(匿名内部类)btn.setOnAction(new javafx.event.EventHandler<ActionEvent>() {@Overridepublic 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;@Overridepublic 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); // 垂直布局,间距10root.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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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 {@Overridepublic void start(Stage primaryStage) {// 创建不同样式的LabelLabel 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;@Overridepublic 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用于接收用户的单行文本输入PasswordFieldTextField的子类,专门用于输入密码,输入的内容会被掩盖。

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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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; // 当前打开的文件路径@Overridepublic 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;@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 程序奠定基础。后续可结合布局管理器(如GridPaneBorderPane)和样式表(CSS)进一步优化界面设计。

http://www.lryc.cn/news/605637.html

相关文章:

  • Vibe Coding:AI驱动开发的安全暗礁与防护体系
  • 异步I/O和同步I/O
  • Day15--二叉树--222. 完全二叉树的节点个数,110. 平衡二叉树,257. 二叉树的所有路径,404. 左叶子之和
  • 在Linux中创建LVGL应用
  • Kotlin -> 普通Lambda vs 挂起Lambda
  • 【Django】-1- 开发项目搭建
  • Django模型迁移指南:从命令用法到最佳实践
  • HttpServletRequest详细解释
  • 如何在NPM上发布自己的React组件(包)
  • 人工智能概念之十一:常见的激活函数与参数初始化
  • Cesium 快速入门(四)相机控制完全指南
  • langchain--1--prompt、output格式、LCEL示例
  • 【烧脑算法】Dijkstra 算法:解决最短路问题
  • 会议室预定系统核心技术:如何用一行SQL解决时间冲突检测难题
  • LLC电源原边MOS管DS增加RC吸收对ZVS的影响分析
  • ode with me是idea中用来干嘛的插件
  • 山东移动云主机:技术架构与特性解析
  • AI 安监系统:为工业园安全保驾护航
  • 1 机器学习概述 (第一天2025.7.31)
  • RabbitMQ 队列配置设置 RabbitMQ 消息监听器的并发消费者数量java
  • Java 大视界 -- Java 大数据在智能医疗远程健康监测与疾病预防预警中的应用(374)
  • Linux 进程管理与计划任务
  • linux git ssh配置过程
  • React中的this绑定
  • SpringMVC的核心架构与请求处理流程
  • PostgreSQL dblink 与 Spring Boot @Transactional 的事务整合
  • 网络层概述
  • AngularJS 事件
  • Web 开发 08
  • 智慧社区项目开发(四)——前后端登录认证相关功能实现解析