【问题标题】:ObservableValue change listeners: how to accessObservableValue 更改监听器:如何访问
【发布时间】:2015-11-25 09:06:27
【问题描述】:

我是 JavaFX 的新手,并且已经开始转换用 Swing 编写的自定义组件。作为最佳实践,我总是检查目标对象的侦听器中是否已包含事件侦听器(PropertyChangeListener、MouseListener、ActionListener 等),以确保不会将同一个侦听器添加两次。我正在尝试对 JavaFX 执行相同的操作,但找不到任何访问侦听器列表的方法(例如,运行 list.contains(listener) 检查)。

我在寻找不存在的东西吗?如果是这样,JavaFX 不包含此功能是否有充分的理由(恕我直言,它应该)?

感谢您的反馈!

【问题讨论】:

  • AFAIK 无法访问 JavaFX 中的内容。你能举例说明你为什么需要它们(或认为你需要)吗?我从来没有遇到过这样做的需要。
  • 正如我的问题中提到的,我试图避免多次将同一对象注册为侦听器。可能发生这种情况的用例在附加评论中,但相信我这是可能的,当它发生时,事件被触发到侦听器的次数与侦听器出现在事件侦听器列表中的次数一样多。低效且不必要,因为监听器几乎总是(99.9999999% 的时间)需要处理事件
  • 是的,我明白:我从来没有遇到过您不知道听众是否已经注册的情况。这就是我想知道的用例。
  • .. 只处理一次事件。另外——这引起了我的注意——JavaFX 文档特别指出,如果一个监听器被注册了不止一次,那么调用 [list].removeListener() 只会删除列表中的第一个条目。换句话说,如果您认为您已经删除了一个侦听器,并且它以某种方式被添加到列表中两次,您可能想知道为什么侦听器仍然处理该事件。
  • 您可能总是在添加侦听器之前删除它。来自removeListener javadoc如果给定的侦听器之前没有注册过(即从未添加过),那么这个方法调用是无操作的。他们在框架中这样做,例如在com.sun.javafx.binding.ContentBinding.bind() 方法中。

标签: javafx changelistener


【解决方案1】:

公共 API 中没有访问 JavaFX 中属性的侦听器列表的机制。

我不确定我是否真的认为需要这样做。您的代码可以控制何时添加和删除侦听器,因此您基本上总是“知道”何时添加了侦听器。在更广泛的意义上,您的 UI 或 UI 组件始终是某种形式的数据的表示,因此监听器是否注册只是这些数据的函数。

举个具体的例子,考虑 cmets 中引用的用例:

public class CustomComponent extends BorderPane {

    private final Button button = new Button("Button");
    private final TextField textField = new TextField();

    private final ObjectProperty<Orientation> orientation = new SimpleObjectProperty<>();

    public ObjectProperty<Orientation> orientationProperty() {
        return orientation ;
    }

    public final Orientation getOrientation() {
        return orientationProperty().get();
    }

    public final void setOrientation(Orientation orientation) {
        orientationProperty().set(orientation);
    }

    public CustomControl(Orientation orientation) {
        setCenter(textField);

        ChangeListener<Number> widthBindingListener = (obs, oldWidth, newWidth) ->
            button.setPrefWidth(newWidth.doubleValue());

        orientationProperty().addListener((obs, oldOrientation, newOrientation) -> {
            if (newOrientation == Orientation.HORIZONTAL) {
                textField.widthProperty().removeListener(widthBindingListener);
                button.setPrefWidth(Control.USE_COMPUTED_SIZE);
                setTop(null);
                setLeft(button);
            } else {
                textField.widthProperty().addListener(widthBindingListener);
                button.setPrefWidth(textField.getWidth());
                setLeft(null);
                setTop(button);
            }
        }

        setOrientation(orientation);

    }

    public CustomControl() {
        this(Orientation.VERTICAL);
    }

    // other methods etc....
}

在此示例中,您可能只使用绑定而不是侦听器:

button.prefWidthProperty().bind(Bindings
    .when(orientationProperty().isEqualTo(Orientation.HORIZONTAL))
    .then(Control.USE_COMPUTED_SIZE)
    .otherwise(textField.widthProperty()));

但这并不能证明这个概念......

请注意,就像在@Clemens 评论中一样,您始终可以确保侦听器只使用该习语注册一次:

textField.widthProperty().removeListener(widthBindingListener);
textField.widthProperty().addListener(widthBindingListener);

但这对我来说似乎不是一个很好的做法:removeListener 涉及遍历侦听器列表(如果尚未添加,整个侦听器列表)。此迭代是不必要的,因为该信息已在其他地方可用。

【讨论】:

  • 感谢您的回复,在阅读您的示例 b/c 时不得不仔细阅读它与我自己的代码的相似之处是不可思议的。您在外出散步时提出重新绑定的建议,这可能是这种特殊情况下最有效的解决方案。原始问题的原因是在使用 Swing 时开发的事件模型,它是许多其他模型的祖先并且运行良好。在切换到 JavaFX 时,我试图从 Swing 中保留好东西(至少是进程)并将它们转换为新格式。当那不可能时,我想知道为什么。
  • 我认为我们可能不同意公开听众列表是否是 Swing 的“好东西”的一部分。对我来说,这有点代码味道 ("Inappropriate intimacy")。如果你有addListenerremoveListener方法,为什么需要暴露列表;或者,相反,如果您要公开列表,为什么还要使用其他方法?对 Richard Bair(JavaFX 的首席架构师和 Swing 领域的杰出人物)进行了一次有趣的采访,他说“在 Swing 中,我们破坏了很多人的代码”。 (续...)
  • 我认为 JavaFX 虽然远非完美,但在良好的 OOP 方面在 Swing 上改进了很多(主要是在每个版本的第一个版本之间理解的概念):你会看到更多 final 方法在旨在强制执行类不变量的 JavaFX 中,您会发现您可以编写 JavaFX,而对子类化现有类的依赖要少得多,并且(与您的问题相关)对真正实现细节的属性的暴露更少。您可能需要更加努力地编写代码,但最终您会得到更好的代码。
  • 关于您的问题“...为什么需要公开列表;或者相反,如果您要公开列表,为什么还要使用其他方法?”在我之前对自定义组件的 Swing 工作中,有一些实例需要断开侦听器以更改特定实例中的值而不通知侦听器,而这些侦听器几乎总是(99.9999% 的时间)在组件外部。更改后,它们将重新连接。这是由设计因素决定的。其他设计不需要此步骤,但会带来其他成本。 (续...)
  • 关于“...公开列表,[then] 为什么要使用附加方法 [add/removeListener]”,必须注意 Swing 事件模型使侦听器可以作为 ARRAY 访问,.. 不是一个列表,恕我直言,数组比列表更难访问和处理。即使将数组转换为列表也不意味着对列表所做的更改将反映在由组件或组件的事件管理支持类维护的侦听器数组中。再次感谢您的意见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-22
相关资源
最近更新 更多