JavaFX 基本控件
最后修改于 2023 年 10 月 18 日
控件是应用程序的基本构建块。Control
是场景图中可以由用户操作的节点。它支持常见的用户交互,其方式对用户来说一致且可预测。JavaFX 拥有广泛的内置控件。在本章中,我们将介绍五个控件:Label
、CheckBox
、ChoiceBox
、Slider
和 ProgressBar
。 ImageView
和 TextField
控件也会简要提及。
JavaFX 标签
Label
是一个不可编辑的文本控件。标签可以使用省略号或截断来调整字符串的大小以适应。
package com.zetcode; import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class LabelEx extends Application { String lyrics = "It's way too late to think of\n" + "Someone I would call now\n" + "And neon signs got tired\n" + "Red eye flights help the stars out\n" + "I'm safe in a corner\n" + "Just hours before me\n" + "\n" + "I'm waking with the roaches\n" + "The world has surrendered\n" + "I'm dating ancient ghosts\n" + "The ones I made friends with\n" + "The comfort of fireflies\n" + "Long gone before daylight\n" + "\n" + "And if I had one wishful field tonight\n" + "I'd ask for the sun to never rise\n" + "If God leant his voice for me to speak\n" + "I'd say go to bed, world\n" + "\n" + "I've always been too late\n" + "To see what's before me\n" + "And I know nothing sweeter than\n" + "Champaign from last New Years\n" + "Sweet music in my ears\n" + "And a night full of no fears\n" + "\n" + "But if I had one wishful field tonight\n" + "I'd ask for the sun to never rise\n" + "If God passed a mic to me to speak\n" + "I'd say stay in bed, world\n" + "Sleep in peace"; @Override public void start(Stage stage) { initUI(stage); } private void initUI(Stage stage) { var root = new HBox(); root.setPadding(new Insets(10)); var lbl = new Label(lyrics); root.getChildren().add(lbl); var scene = new Scene(root); stage.setTitle("No sleep"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
该示例显示了 Cardigans 乐队的一首歌曲的歌词。
String lyrics = "It's way too late to think of\n" + "Someone I would call now\n" + "And neon signs got tired\n" + "Red eye flights help the stars out\n" ...
该字符串由多行文本组成。
var root = new HBox(); root.setPadding(new Insets(10));
标签控件放置在一个 HBox
中。我们在框周围添加了一些填充。
var lbl = new Label(lyrics);
创建了一个 Label
控件。它将字符串作为其唯一参数。
root.getChildren().add(lbl);
标签已添加到容器中。
labelFor 属性
labelFor
属性指定一个节点,如果按下助记符,焦点将发送到该节点。
package com.zetcode; import javafx.application.Application; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; import javafx.stage.Stage; public class LabelForEx extends Application { @Override public void start(Stage stage) { initUI(stage); } private void initUI(Stage stage) { var root = new GridPane(); root.setVgap(10); root.setHgap(5); root.setPadding(new Insets(10)); var lbl1 = new Label("_Name:"); var lbl2 = new Label("_Address:"); var lbl3 = new Label("_Occupation:"); var field1 = new TextField(); var field2 = new TextField(); var field3 = new TextField(); lbl1.setLabelFor(field1); lbl1.setMnemonicParsing(true); lbl2.setLabelFor(field2); lbl2.setMnemonicParsing(true); lbl3.setLabelFor(field3); lbl3.setMnemonicParsing(true); root.add(lbl1, 0, 0); root.add(field1, 2, 0); root.add(lbl2, 0, 1); root.add(field2, 2, 1); root.add(lbl3, 0, 2); root.add(field3, 2, 2); GridPane.setHalignment(lbl1, HPos.RIGHT); GridPane.setHalignment(lbl2, HPos.RIGHT); GridPane.setHalignment(lbl3, HPos.RIGHT); var scene = new Scene(root); stage.setTitle("TextField"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
该示例使用 labelFor
属性和助记符将焦点转移到指定的文本字段。
var root = new GridPane(); root.setVgap(10); root.setHgap(5); root.setPadding(new Insets(10));
我们的应用程序是一个典型的基于表单的程序。 GridPane
非常适合此。我们在控件周围和控件之间设置了一些空间。
var lbl1 = new Label("_Name:"); var lbl2 = new Label("_Address:"); var lbl3 = new Label("_Occupation:");
创建了三个 Labels
。下划线字符位于助记键之前。
var field1 = new TextField(); var field2 = new TextField(); var field3 = new TextField();
TextField
是一个用于编辑单行未格式化文本的控件。每个文本字段都放置在一个标签控件旁边。
lbl1.setLabelFor(field1);
setLabelFor
设置一个目标节点,当按下助记符时,焦点将转移到该节点。
lbl1.setMnemonicParsing(true);
默认情况下,标签不设置助记符。我们必须使用 setMnemonicParsing
方法启用它们。

在某些平台上,有必要按 mouseless 修饰符(通常是 Alt)才能显示下划线。在图中,焦点通过按 Alt+A 转移到中间文本字段。
JavaFX 复选框
CheckBox
是一个三态选择控件框,在选中时显示复选标记或勾号。该控件默认具有两种状态:已选中和未选中。setAllowIndeterminate
启用第三种状态:不确定。
package com.zetcode; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.CheckBox; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class CheckBoxEx extends Application { @Override public void start(Stage stage) { initUI(stage); } private void initUI(Stage stage) { var root = new HBox(); root.setPadding(new Insets(10, 0, 0, 10)); var cbox = new CheckBox("Show title"); cbox.setSelected(true); cbox.setOnAction((ActionEvent event) -> { if (cbox.isSelected()) { stage.setTitle("CheckBox"); } else { stage.setTitle(""); } }); root.getChildren().add(cbox); var scene = new Scene(root, 300, 200); stage.setTitle("CheckBox"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
该示例根据是否选中复选框显示或隐藏窗口标题。
var cbox = new CheckBox("Show title");
创建了一个 CheckBox
控件。指定的文本是它的标签。
cbox.setSelected(true);
由于窗口的标题默认可见,我们使用 setSelected
方法选中该控件。
cbox.setOnAction((ActionEvent event) -> { if (cbox.isSelected()) { stage.setTitle("CheckBox"); } else { stage.setTitle(""); } });
使用 setOnAction
方法,我们设置复选框的动作,当复选框被触发时会调用该动作。我们使用 isSelected
方法确定其状态。根据当前状态,我们使用 setTitle
方法显示或隐藏窗口标题。

注意复选框文本周围的蓝色矩形。它表示此控件具有键盘焦点。可以使用 Space 键选择和取消选择复选框。
JavaFX 滑块
Slider
是一个控件,它允许用户通过在有界间隔内滑动旋钮来以图形方式选择一个值。滑块可以选择性地显示刻度线和标签,指示不同的滑块位置值。
package com.zetcode; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Slider; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class SliderEx extends Application { private ImageView iview; private Image muteImg; private Image minImg; private Image maxImg; private Image medImg; @Override public void start(Stage stage) { initUI(stage); } private void initUI(Stage stage) { var root = new HBox(10); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(15)); loadImages(); iview = new ImageView(muteImg); var slider = new Slider(0, 100, 0); slider.valueProperty().addListener(new MyChangeListener()); var scene = new Scene(root); root.getChildren().addAll(slider, iview); stage.setTitle("Slider"); stage.setScene(scene); stage.show(); } private void loadImages() { muteImg = new Image("file:mute.png"); minImg = new Image("file:min.png"); maxImg = new Image("file:max.png"); medImg = new Image("file:med.png"); } private class MyChangeListener implements ChangeListener<Number> { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { double value = newValue.doubleValue(); if (value == 0) { iview.setImage(muteImg); } else if (value > 0 && value <= 30) { iview.setImage(minImg); } else if (value > 30 && value < 80) { iview.setImage(medImg); } else { iview.setImage(maxImg); } } } public static void main(String[] args) { launch(args); } }
在代码示例中,我们显示了一个 Slider
和一个 ImageView
控件。通过拖动滑块的旋钮,我们更改标签控件上的图像。
root.setAlignment(Pos.CENTER);
滑块和图像视图在行中居中。
iview = new ImageView(muteImg);
ImageView
显示使用 Image
类加载的图像。
var slider = new Slider(0, 100, 0);
使用指定的最大值、最小值和当前值创建一个 Slider
控件。
slider.valueProperty().addListener(new MyChangeListener());
将侦听器添加到滑块的值更改中。
double value = newValue.doubleValue(); if (value == 0) { iview.setImage(muteImg); } else if (value > 0 && value <= 30) { iview.setImage(minImg); } else if (value > 30 && value < 80) { iview.setImage(medImg); } else { iview.setImage(maxImg); }
基于滑块的当前值,我们将适当的图像设置为图像视图。
private void loadImages() { muteImg = new Image("file:mute.png"); minImg = new Image("file:min.png"); maxImg = new Image("file:max.png"); medImg = new Image("file:med.png"); }
loadImages
方法从磁盘加载图像。

JavaFX ChoiceBox
ChoiceBox
用于向用户呈现一小组预定义的选项。当用户单击该框时,将显示一个选项列表。一次只能选择一个选项。当此列表未显示时,将显示当前选定的选项。ChoiceBox
项目选择由 SelectionModel
处理。
package com.zetcode; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; import javafx.scene.control.SingleSelectionModel; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class ChoiceBoxEx extends Application { @Override public void start(Stage stage) { initUI(stage); } private void initUI(Stage stage) { var root = new VBox(35); root.setPadding(new Insets(10)); var lbl = new Label(); var chbox = new ChoiceBox<>(FXCollections.observableArrayList( "Ubuntu", "Redhat", "Arch", "Debian", "Mint")); SingleSelectionModel<String> model = chbox.getSelectionModel(); model.selectedItemProperty().addListener((observableValue, s, t1) -> lbl.setText(t1)); root.getChildren().addAll(chbox, lbl); var scene = new Scene(root, 300, 250); stage.setTitle("ChoiceBox"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
在我们的示例中,我们有一个选择框和一个标签。选择框包含一个字符串列表,表示 Linux 发行版的名称。从选择框中选择的项显示在标签中。
var lbl = new Label();
此 Label
显示当前从选择框中选择的项。
var chbox = new ChoiceBox<>(FXCollections.observableArrayList( "Ubuntu", "Redhat", "Arch", "Debian", "Mint"));
创建了一个 ChoiceBox
。它将一个可观察的数组列表作为参数。
SingleSelectionModel<String> model = chbox.getSelectionModel(); model.selectedItemProperty().addListener((observableValue, s, t1) -> lbl.setText(t1));
要实现一个侦听器,我们需要使用 getSelectionModel
方法获取选择模型。该模型包含可观察的 selectedItem
属性。在处理程序方法中,我们获取选定的值并将其设置为标签。

JavaFX 进度条
ProgressBar
是一个控件,它通过完成条指示特定任务的处理。
package com.zetcode; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ProgressBar; import javafx.scene.layout.HBox; import javafx.stage.Stage; import javafx.util.Duration; public class ProgressBarEx extends Application { @Override public void start(Stage stage) { initUI(stage); } private void initUI(Stage stage) { var root = new HBox(15); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(10)); var pbar = new ProgressBar(0); pbar.setPrefWidth(150); var frame1 = new KeyFrame(Duration.ZERO, new KeyValue(pbar.progressProperty(), 0)); var frame2 = new KeyFrame(Duration.seconds(3), new KeyValue(pbar.progressProperty(), 1)); var task = new Timeline(frame1, frame2); var btn = new Button("Start"); btn.setOnAction((ActionEvent actionEvent) -> task.playFromStart()); root.getChildren().addAll(pbar, btn); var scene = new Scene(root); stage.setTitle("ProgressBar"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
该示例由一个进度条和一个按钮组成。该按钮启动进度条,该进度条动画几秒钟。
var pbar = new ProgressBar(0);
构造函数使用给定的进度值创建一个新的 ProgressBar
。
var frame1 = new KeyFrame(Duration.ZERO, new KeyValue(pbar.progressProperty(), 0)); var frame2 = new KeyFrame(Duration.seconds(3), new KeyValue(pbar.progressProperty(), 1)); var task = new Timeline(frame1, frame2);
此代码创建一个简单的动画任务。动画由两个帧组成。动画属性定义为 KeyValues
。
var btn = new Button("Start"); btn.setOnAction((ActionEvent actionEvent) -> task.playFromStart());
当触发时,按钮调用 playFromStart
方法,该方法从初始位置以正向播放动画。

在 JavaFX 教程的这一部分中,我们介绍了基本的 JavaFX 控件。