JavaFX初学

1. Scene Graph(场景图)

JavaFX 的场景图是一个树状数据结构,表示应用程序的所有 UI 组件。

层次结构:
Stage (舞台) → Scene (场景) → Parent (根节点) → Nodes (子节点)

2. 事件机制

JavaFX 使用事件驱动编程模型,基于观察者模式。

主要事件类型:

  • ActionEvent:按钮点击等
  • MouseEvent:鼠标相关事件
  • KeyEvent:键盘事件
  • WindowEvent:窗口事件

3. 常用容器和布局方法

VBox - 垂直排列

VBox vbox = new VBox(10); // 10像素间距
vbox.setPadding(new Insets(15)); // 内边距
vbox.setAlignment(Pos.CENTER);

HBox - 水平排列

HBox hbox = new HBox(10);
hbox.setPadding(new Insets(10));
hbox.setAlignment(Pos.CENTER);

BorderPane

BorderPane borderPane = new BorderPane();
// 顶部区域
HBox topBox = new HBox();
topBox.getChildren().add(new Label("顶部导航栏"));
borderPane.setTop(topBox);
// 左侧区域
VBox leftBox = new VBox(10);
leftBox.getChildren().addAll(new Button("菜单1"),new Button("菜单2"),new Button("菜单3"));
borderPane.setLeft(leftBox);
// 中心区域
borderPane.setCenter(new Label("主要内容区域"));
// 右侧区域
borderPane.setRight(new Label("右侧边栏"));
// 底部区域
borderPane.setBottom(new Label("底部状态栏"));

GridPane

GridPane gridPane = new GridPane();
gridPane.setPadding(new Insets(20));
gridPane.setHgap(10); // 水平间距
gridPane.setVgap(10); // 垂直间距

Label nameLabel = new Label("姓名:");
TextField nameField = new TextField();
Label emailLabel = new Label("邮箱:");
TextField emailField = new TextField();
Button submitBtn = new Button("提交");
Button cancelBtn = new Button("取消");

// 指定行列位置
gridPane.add(nameLabel, 0, 0); // 列0, 行0
gridPane.add(nameField, 1, 0); // 列1, 行0
gridPane.add(emailLabel, 0, 1); // 列0, 行1
gridPane.add(emailField, 1, 1); // 列1, 行1
gridPane.add(submitBtn, 0, 2); // 列0, 行2
gridPane.add(cancelBtn, 1, 2); // 列1, 行2

4. 常用控件

Label(标签)

用于显示不可编辑的文本或图像。

Label label = new Label("文本内容");
label.setFont(new Font("Arial", 16));
label.setWrapText(true);

Button(按钮)

触发动作的交互控件。

Button button = new Button("点击我");
button.setOnAction(e -> {
// 处理点击事件
});

Radio Button(单选按钮)

成组使用的互斥选择控件。

ToggleGroup group = new ToggleGroup();
RadioButton radio1 = new RadioButton("选项1");
RadioButton radio2 = new RadioButton("选项2");
radio1.setToggleGroup(group);
radio2.setToggleGroup(group);

Checkbox(复选框)

多选选择控件。

CheckBox checkBox = new CheckBox("同意条款");
checkBox.setAllowIndeterminate(true);
checkBox.setSelected(true);

Text Field(文本框)

单行文本输入。

TextField textField = new TextField();
textField.setPromptText("请输入...");

Scroll Pane(滚动面板)

为内容提供滚动视图。

scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);

List View(列表视图)

显示可滚动项目列表。

ListView<String> listView = new ListView<>();
ObservableList<String> items = FXCollections.observableArrayList("项目1", "项目2");
listView.setItems(items);

Table View(表格视图)

以表格形式显示数据。

TableView<ObservableList<String>> tableView = new TableView<>();
TableColumn<String, String> col1 = new TableColumn<>("列1");
tableView.getColumns().addAll(col1);

Tree View(树视图)

以层次结构显示数据。

Combo Box(组合框)

可编辑的下拉选择控件。

ComboBox<String> comboBox = new ComboBox<>();
comboBox.setEditable(true);

Slider(滑块)

数值范围选择控件。

Slider slider = new Slider(0, 100, 50);
slider.setShowTickLabels(true);
slider.setShowTickMarks(true);

Progress Bar and Progress Indicator(进度条和进度指示器)

ProgressBar progressBar = new ProgressBar(0.5);
ProgressIndicator indicator = new ProgressIndicator(0.7);

Hyperlink(超链接)

Hyperlink link = new Hyperlink("点击访问");
link.setOnAction(e -> {
// 打开链接
});

Tooltip(提示信息)

鼠标悬停显示的帮助信息。

Tooltip tooltip = new Tooltip("这是提示信息");
button.setTooltip(tooltip);

标准菜单系统。

MenuBar menuBar = new MenuBar();
Menu fileMenu = new Menu("文件");
MenuItem openItem = new MenuItem("打开");
fileMenu.getItems().add(openItem);
menuBar.getMenus().add(fileMenu);

Color Picker(颜色选择器)

ColorPicker colorPicker = new ColorPicker();
Color color = colorPicker.getValue();

Date Picker(日期选择器)

DatePicker datePicker = new DatePicker();
datePicker.setValue(LocalDate.now());

File Chooser(文件选择器)

FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("打开文件");
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("文本文件", "*.txt"));
File file = fileChooser.showOpenDialog(stage);

5. 技术要点

5.1 事件处理

大多数控件都支持 setOnAction()setOnMouseClicked() 等事件处理器方法。

5.2 数据绑定

  • 使用 ObservableList 绑定列表数据
  • 使用属性(Property)实现双向绑定

5.3 CSS样式

// 代码中设置样式
control.setStyle("-fx-background-color: #FF0000;");
// 加载外部CSS
scene.getStylesheets().add("path/to/styles.css");

5.4 单元格工厂

用于自定义列表、表格等控件的渲染:

listView.setCellFactory(param -> new ListCell<String>() {    
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
// 自定义渲染逻辑
}
});

三、FXML

FXML是JavaFX的UI定义语言,了解标签和类的关系以及相关属性值的使用。

四、Binding

定义属性

import javafx.beans.property.DoubleProperty; 
import javafx.beans.property.SimpleDoubleProperty;

class Bill {
private DoubleProperty amountDue = new SimpleDoubleProperty();

public final double getAmountDue(){return amountDue.get();}
public final void setAmountDue(double value){amountDue.set(value);}
public DoubleProperty amountDueProperty() {return amountDue;}
}
  • get()返回的是double,不是doubleProperty
  • set()参数为double
  • xxProperty()返回property类型

使用Bindings类

import javafx.beans.property.IntegerProperty; 
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.binding.NumberBinding;
import javafx.beans.binding.Bindings;

public class Main {
public static void main(String[] args) {
IntegerProperty num1 = new SimpleIntegerProperty(1);
IntegerProperty num2 = new SimpleIntegerProperty(2);
IntegerProperty num3 = new SimpleIntegerProperty(3);
IntegerProperty num4 = new SimpleIntegerProperty(4);
NumberBinding total = Bindings.add(num1.multiply(num2), num3.multiply(num4));
System.out.println(total.getValue());
num1.setValue(2);
System.err.println(total.getValue());
}
}

失效监听和变更监听

  • ChangeListener(变更监听器):只有当属性值实际发生改变时才会触发
  • InvalidationListener(失效监听器):当属性值变为无效状态时触发

五、集合

JavaFX集合框架(javafx.collections包)提供了可观察的集合类型。

主要接口和类

接口:

  • ObservableList - 可观察的List
  • ObservableMap - 可观察的Map
  • ListChangeListener - List变更监听器
  • MapChangeListener - Map变更监听器

类:

  • FXCollections - 工具类
  • ListChangeListener.Change - List变更详细信息封装
  • MapChangeListener.Change - Map变更详细信息封装

创建可观察集合

// 从普通List创建
List<String> normalList = new ArrayList<>();
ObservableList<String> observableList = FXCollections.observableList(normalList);

// 从普通Map创建
Map<String, String> normalMap = new HashMap<>();
ObservableMap<String, String> observableMap = FXCollections.observableMap(normalMap);

添加监听器

// ObservableList监听
observableList.addListener((ListChangeListener) change -> {
while (change.next()) {
if (change.wasAdded()) {
// 处理添加操作
}
if (change.wasRemoved()) {
// 处理删除操作
}
if (change.wasPermutated()) {
// 处理重排操作
}
if (change.wasReplaced()) {
// 处理替换操作
}
}
});

// ObservableMap监听
observableMap.addListener((MapChangeListener) change -> {
// 处理变更
});

注意事项:

  1. 只有通过ObservableList/Map本身进行的修改才会触发监听
  2. FXCollections的方法会产生最少的变更通知
  3. 处理List变更时必须使用循环遍历
  4. MapChangeListener.Change通常只包含一个变更,不需要循环