JavaFX初学
1. Scene Graph(场景图)
JavaFX 的场景图是一个树状数据结构,表示应用程序的所有 UI 组件。
层次结构:
Stage (舞台) → Scene (场景) → Parent (根节点) → Nodes (子节点)
2. 事件机制
JavaFX 使用事件驱动编程模型,基于观察者模式。
主要事件类型:
- ActionEvent:按钮点击等
- MouseEvent:鼠标相关事件
- KeyEvent:键盘事件
- WindowEvent:窗口事件
3. 常用容器和布局方法
VBox - 垂直排列
VBox vbox = new VBox(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); gridPane.add(nameField, 1, 0); gridPane.add(emailLabel, 0, 1); gridPane.add(emailField, 1, 1); gridPane.add(submitBtn, 0, 2); gridPane.add(cancelBtn, 1, 2);
|
4. 常用控件
Label(标签)
用于显示不可编辑的文本或图像。
Label label = new Label("文本内容"); label.setFont(new Font("Arial", 16)); label.setWrapText(true);
|
触发动作的交互控件。
Button button = new Button("点击我"); button.setOnAction(e -> { });
|
成组使用的互斥选择控件。
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("请输入...");
|
为内容提供滚动视图。
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 = 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;");
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<String> normalList = new ArrayList<>(); ObservableList<String> observableList = FXCollections.observableList(normalList);
Map<String, String> normalMap = new HashMap<>(); ObservableMap<String, String> observableMap = FXCollections.observableMap(normalMap);
|
添加监听器
observableList.addListener((ListChangeListener) change -> { while (change.next()) { if (change.wasAdded()) { } if (change.wasRemoved()) { } if (change.wasPermutated()) { } if (change.wasReplaced()) { } } });
observableMap.addListener((MapChangeListener) change -> { });
|
注意事项:
- 只有通过ObservableList/Map本身进行的修改才会触发监听
- FXCollections的方法会产生最少的变更通知
- 处理List变更时必须使用循环遍历
- MapChangeListener.Change通常只包含一个变更,不需要循环