【问题标题】:Best practice since JavaFX setters/getters are final?JavaFX setter/getter 是最终的最佳实践?
【发布时间】:2015-01-20 22:46:51
【问题描述】:

在将 Windows 应用程序从 Swing 转换为 JavaFX 的过程中。例如,我们有很多以前覆盖 JTextField 的 mutator 方法的自定义组件。在 JavaFX 中,这些方法被声明为 final。我应该只创建调用最终方法并在前后修改值的包装器方法吗?我只是想确保我从一开始就以正确的方式去做。

编辑:我还将包括其中一些代码来自更旧版本的 Java 的事实。因此,某些事情可能不再需要。这是我们在自定义 JFormattedTextField 中使用的 setText 方法的示例:

public void setText(String text) {
    String newString = "";
    if (mask == null) {
        newString = text;
        unformattedCurrent = newString;
    } else {
        newString = applyMask(text);
    }
    super.setText(newString);
    if (text.trim().length() == 0) {
        positionCaret(0);
        selectRange(0, 0);
    } else {
        int length = getFormattedText().length();
        if (isFocused() && length == getCaretPosition()) {
            super.selectAll();
        }
    }
}

【问题讨论】:

  • 您之前所做的一些代码会很有用(以及对该代码的作用的一些解释)
  • JavaFX 可能试图让你更多地使用它的模型层......

标签: java swing javafx setter getter


【解决方案1】:

使用侦听器来通知突变状态的变化

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 库可能有一些很好的方法来处理您想要实现的模式。

【讨论】:

  • 感谢您的帮助!因此,在更改侦听器中,我会在设置任何掩码后设置适当的值?改变吸气剂怎么样?因此,对于这些带有掩码的字段,getText 方法基本上会剥离掩码值并仅返回基础值。比如:public String getText() { return removeMask(super.getText()); }
  • 所以经过一番思考。不会在侦听器中设置值创建一个无限循环,因为它会在更新值时继续进入 ChangeListener。
  • 再次感谢您的所有帮助。对这个问题有什么建议吗? stackoverflow.com/questions/28058142/…
  • 不,Thomas - 我很少混合 JavaFX 和 Swing,我不明白你的相关问题。
猜你喜欢
  • 2014-11-18
  • 2017-02-17
  • 1970-01-01
  • 2020-05-20
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多