使用侦听器来通知突变状态的变化
JavaFX 公共 API 中公开的几乎所有内容都是属性,因此挂钩变异操作的一种方法是添加 ChangeListeners(或 InvalidationListeners)。
更多信息请参见JavaFX property documentation(以下 sn-p 是从那里复制的):
import javafx.beans.value.ObservableValue;
import javafx.beans.value.ChangeListener;
public class Main {
public static void main(String[] args) {
Bill electricBill = new Bill();
electricBill.amountDueProperty().addListener(new ChangeListener() {
@Override public void changed(
ObservableValue o,
Object oldVal,
Object newVal
) {
System.out.println("Electric bill has changed!");
}
});
electricBill.setAmountDue(100.00);
}
}
对于 Java 8+,你可以这样写:
Label billAmountDue = new Label();
billAmountDue.textProperty().addListener((observable, oldVal, newVal) ->
System.out.println("Electric bill has changed!")
);
如果您是子类化,您可以在子类的构造函数中设置监听器。当您覆盖 setter 函数时,这些侦听器可以执行类似于您在 Swing 类中所做的工作:
public class AmountLabel extends Label {
public AmountLabel(String text) {
super(text);
textProperty().addListener(observable ->
System.out.println("Amount invalidated.")
);
}
}
其他问题的答案
所以在更改侦听器中,我会在设置任何掩码后设置适当的值?
嗯,你可以,我以前做过。我不确定这样做是否是最佳做法(尽管我不知道一般处理它的另一种方法)。我确实记得 JavaFX 属性设计器在某个论坛上说他们并没有真正为更改侦听器中的更新设计属性机制,所以我想这不能保证。
我遇到了在更改侦听器中更改 WebView 的 location 属性导致线程问题的问题,但我认为由于 WebView 的复杂性,这是一个非常不寻常的情况 - 我提交了一份已接受的关于该特定问题的错误报告案子。
对于其他控件和属性,您可能可以使用“在侦听器中更新”方法(尽管请参阅下一个后续问题答案中的注意事项)。
不会在侦听器中设置值创建一个无限循环,因为它会在更新值时继续进入 ChangeListener。
不,我不认为它会创建一个无限循环。我认为 JavaFX 侦听器实现中有一些东西可以阻止它无限循环。我还没有检查源代码以查看它是什么,如果我的记忆不正确,您需要实现特定的逻辑来防止递归,这将是完全不可取的,因为它会使事情变得不必要地复杂化。
如果您在更改侦听器中设置属性的值,由于怀疑短路逻辑可以防止无限递归,我真的不知道更改后的值是否会传播到所有侦听器或绑定到该属性的项目属性(您需要检查实现或编写一些测试来检查行为)。
这就是为什么我说虽然它在大多数情况下可能会起作用,但在更改侦听器中设置值可能不是最佳实践的几个原因。
改变吸气剂怎么样?
这个用例似乎比监听设定值的频率低。
我建议创建一个新属性并设置一个bidirectional binding with a string converter。字符串转换器的使用是可选的,但它显示了如何监听转换后的值。
特定于格式化文本字段
您的问题很笼统,但您在帖子中讨论的问题是针对格式化文本字段的。一般情况是我之前试图回答的。对于格式化文本字段的特定情况,有可能适用的特定解决方案。
Richard recommends using a different API altogether, replaceText 仅适用于 TextInput:
field = new TextField() {
@Override public void replaceText(int start, int end, String text) {
// If the replaced text would end up being invalid, then simply
// ignore this call!
if (!text.matches("[a-z]")) {
super.replaceText(start, end, text);
}
}
@Override public void replaceSelection(String text) {
if (!text.matches("[a-z]")) {
super.replaceSelection(text);
}
}
};
Richard 的帖子现在已经很老了。对于Java 8u40,将包含以下功能:
另外一个相关的open-jfx实现项是:
目前不是 9,但一旦实施,可能有助于为您提出的问题提供一些进一步的具体用例,并在票证上提供开发人员说明“我们相信为 8u40 提供示例代码就足够了”。
第三方JideFX library includes a FormattedTextField class。
关于为什么 JavaFX API 中的东西是最终的一些背景信息
Quote from Richard Bair,首席 JavaFX 开发人员:
一个人的安全。你可以对非最终课程做坏事!这只是一般规则(这就是为什么我们通常希望尽我们所能完成所有事情)。在这种特殊情况下,它可能无关紧要。
建议
您可能想要做的不是为文本输入格式化程序之类的东西覆盖 getter,而是为格式掩码、未格式化的值文本和格式化的值文本使用不同的 API。我认为这可能是为了将格式化的 JavaFX TextFields 添加到 Java 8u40 所做的。
看看 EasyBind
第三方EasyBind 库可能有一些很好的方法来处理您想要实现的模式。