常用内容组件:基本知识

1年前 阅读 1023 评论 0 赞 0

内容组件是所有 JavaFX 控件中最丰富的一类了,它们以丰富的方式展现内容,同时也提供交互性,向用户反映当前的系统状态,以及告诉用户能做些什么样的操作。在熟悉每个内容组件之前,我们应该先了解下它们的一些共性,这样能节省很多学习时间。

数据绑定

任何界面开发都有一个比较麻烦的地方,就是如何保持界面上展现的内容与实际数据时时刻刻保持一致,包括当数据变化时,要在界面上即时展现出来。实现这样的功能不难,但是要写很多代码。

Java FX 设计了一种机制,叫数据绑定。举个例子,我们的界面上有一个 TextField 和一个 Label,我希望当我在 TextField 中输入文本时,Label 中的文本也跟着变。下面是一个 Controller 类的例子,假设我们有一个这样的界面:

2017-09-10_21h19_25

鉴于你已经知道如何编写 FXML 和创建 Controller 了。这个例子中,我将省略启动窗体和解析 FXML 的步骤。什么,你还不会?好吧好吧,我把 FXML 给你看下吧:

  1. <VBox fx:controller="binding.BindingController" xmlns:fx="javafx"
  2. prefWidth="400" style="-fx-padding: 30" spacing="15">
  3. <Label text="Your text:" fx:id="label1"/>
  4. <TextField prefWidth="250" fx:id="textfield1"/>
  5. </VBox>

上面的 binding.BindingController 就是我们的 Controller 类。它的内容目前看起来是这样:

  1. public class BindingController {
  2. public Label label1;
  3. public TextField textfield1;
  4. public void initialize() {
  5. }
  6. }

在以前我们要实现这样的功能,方法是侦听 TextField 的 KeyPress 事件,在事件中获得 TextField 的内容,然后将其赋值给 Label 文本。

但是在 JavaFX 中我们只需要这样:

  1. public void initialize() {
  2. SimpleStringProperty prefix = new SimpleStringProperty("Your text: ");
  3. label1.textProperty().bind(prefix.concat(textfield1.textProperty()));
  4. }

这两句信息量有点多,我们先看下第二行,它的意思是将 label1 的文本属性绑定到另一个文本属性上。将 A 属性绑定到 B 属性意味着,当 B 属性值变化时,A 属性值也会立即变化。

文本属性的类叫做 StringProperty,StringProperty 是一个抽象类,SimpleStringProperty 是它常用的一个实现类。类似的还有 IntegerProperty、DoubleProperty、BooleanProperty 等等,对于一般的类,还有带泛型的 ObjectProperty。

那么另一个文本属性,它是一个组合,是一个由 prefix 这个文本属性和 textfield1 的文本属性通过 concat() 方法组合起来的值。所以我可以根据自己需要,将现有的属性自由组合起来,生成一个新的属性。

这样,当我在 textfield1 中键入内容时,该内容立刻就会在 label1 上显示出来。我不需要编写任何事件处理代码,就能实现这点。

我一直在考虑要不要在教程中加入动态 GIF 来展示运行效果。但鉴于图片体积可能很大,决定暂时不这么做。当然也欢迎读者的反馈。

将对象属性绑定到界面上

StringProperty 等等属性并非只能用在控件上,我们可以在任何地方用它。例如我们创建一个简单的计数器类,就可以用 IntegerProperty:

  1. public class Counter {
  2. private IntegerProperty value = new SimpleIntegerProperty();
  3. public IntegerProperty valueProperty() {
  4. return this.value;
  5. }
  6. public void add() {
  7. this.value.set(this.value.get() + 1);
  8. }
  9. }

基本上,属性类都有 set()get() 方法,用于存取内容。怎么用这个类呢,假设我们有个这样的界面:

2017-09-10_21h46_41

对,这次我就真的把 FXML 省略了。在 Controller 中,我们要加上一个 Counter 作为成员:

  1. public class CounterDemoController {
  2. public Label label1; // 界面上的 label1
  3. public Button button1; // 界面上的 button1
  4. private Counter counter = new Counter(); // 计数器
  5. public void initialize() {
  6. }
  7. }

我们希望每次点击按钮时,counter 的数值加一,然后在 label1 上显示出来。实现的方法很简单:

  1. public void initialize() {
  2. label1.textProperty().bind(counter.valueProperty().asString());
  3. button1.setOnAction(event -> counter.add());
  4. }

可以看到这里对 counter 的 valueProperty() 做了转换,因为 label1 的 textProperty() 是字符串类型的属性,也只能绑定到字符串类型的其他属性上,所以这里用 asString() 方法做了相应的转换。

当点击按钮时,可以看到我们没有对 label1 做任何操作,而只是调用了 counter 的 add() 方法。这就是数据绑定的好处,当我们在属性之间建立好关联之后,我们就可以专注于编写业务逻辑本身,而界面则会根据对象的属性值自动更新。

我们还可以进一步修饰 Counter 类:

  1. public class Counter {
  2. private IntegerProperty value = new SimpleIntegerProperty();
  3. public IntegerProperty valueProperty() {
  4. return this.value;
  5. }
  6. public int getValue() {
  7. return this.value.get();
  8. }
  9. public void setValue(int value) {
  10. this.value.set(value);
  11. }
  12. public void add() {
  13. this.value.set(this.value.get() + 1);
  14. }
  15. }

这样 value 成员看上去就完全像是一个普通的 JavaBean 属性了!但我需要提醒一下,如果你通过数据库来存取对象,最好还是用普通的 JavaBean,因为这些操作是不需要侦听变更的,StringProperty 这些类为了实现属性的绑定、组合和转换,编写了一套复杂的机制(具体后面再介绍),存取效率肯定不如 JavaBean,消耗的内存也更多,所以划不来。数据绑定最好是只用在跟界面打交道的地方。

你的支持将鼓励作者继续创作

评论(0)

(无)