有条件的绑定不能按预期方式工作 [英] Conditional binding doesn't work as expected

查看:79
本文介绍了有条件的绑定不能按预期方式工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

看看Layout-> initPanelSizeBinding().问题是即使panelProperty.get()null,也正在执行then().我在做错什么吗?

Take a look at Layout -> initPanelSizeBinding(). The problem is then() is being executed even though panelProperty.get() is null. Am I doing something wrong?

您可以自己尝试一下,以下是完整的可复制示例. (在OpenJFX 15上测试)

You can try it out yourself, below is the full reproducible example. (Tested on OpenJFX 15)

可复制的示例-单击此处(pastebin)

或在下面看看:

代码:

Main.java

Main.java

public class Main {
 
    public static void main(String[] args) {
        Layout layout = new Layout();
        /*
        WARNING: Exception while evaluating binding
        java.lang.NullPointerException: Cannot invoke "Panel.getWidth()" because the return value of "javafx.beans.property.ObjectProperty.get()" is null
            at Layout.lambda$initPanelSizeBinding$0(Layout.java:20)
            at javafx.beans.binding.Bindings$6.computeValue(Bindings.java:358)
            at javafx.beans.binding.ObjectBinding.get(ObjectBinding.java:157)
            at javafx.beans.binding.ObjectExpression.getValue(ObjectExpression.java:49)
            at com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:53)
            at javafx.beans.binding.ObjectBinding.addListener(ObjectBinding.java:77)
            at javafx.beans.binding.When$ObjectCondition.<init>(When.java:757)
            at javafx.beans.binding.When$ObjectConditionBuilder.otherwise(When.java:854)
            at Layout.initPanelSizeBinding(Layout.java:24)
            at Layout.<init>(Layout.java:12)
            at Main.main(Main.java:4)
         */
    }
 
}

Layout.java

Layout.java

public final class Layout {
 
    public Layout() {
        panelSize.bind(initPanelSizeBinding(panel));
        // there's no need to do anything with panel, because it's an example
    }
 
    private ObjectBinding<Dimension2D> initPanelSizeBinding(ObjectProperty<Panel> panelProperty) {
        return Bindings
                .when(panelProperty.isNotNull())
                .then(Bindings.createObjectBinding(
                        () -> new Dimension2D(panelProperty.get().getWidth(), panelProperty.get().getHeight()),
                        Bindings.selectDouble(panelProperty, "width"),
                        Bindings.selectDouble(panelProperty, "height"))
                )
                .otherwise(new Dimension2D(150.0, 150.0));
    }
 
    //------Properties
 
    //panel
 
    private final ObjectProperty<Panel> panel = new SimpleObjectProperty<>
            (Layout.this, "panel", null);
 
    public ObjectProperty<Panel> panelProperty() {
        return panel;
    }
 
    public void setPanel(Panel value) {
        panel.set(value);
    }
 
    public Panel getPanel() {
        return panel.get();
    }
 
    //panelSize
 
    private final ReadOnlyObjectWrapper<Dimension2D> panelSize = new ReadOnlyObjectWrapper<>
            (Layout.this, "panelSize");
 
    public ReadOnlyObjectProperty<Dimension2D> panelSizeProperty() {
        return panelSize.getReadOnlyProperty();
    }
 
    public Dimension2D getPanelSize() {
        return panelSize.get();
    }
 
}

Panel.java

Panel.java

public final class Panel {
 
    private final VBox rootNode;
 
    public Panel() {
        rootNode = new VBox();
        width.bind(rootNode.widthProperty());
        height.bind(rootNode.heightProperty());
    }
 
    public VBox getRootNode() {
        return rootNode;
    }
 
    //------Properties
 
    //width
 
    private final ReadOnlyDoubleWrapper width = new ReadOnlyDoubleWrapper
            (Panel.this, "width");
 
    public ReadOnlyDoubleProperty widthProperty() {
        return width.getReadOnlyProperty();
    }
 
    public double getWidth() {
        return width.get();
    }
 
    //height
 
    private final ReadOnlyDoubleWrapper height = new ReadOnlyDoubleWrapper
            (Panel.this, "height");
 
    public ReadOnlyDoubleProperty heightProperty() {
        return height.getReadOnlyProperty();
    }
 
    public double getHeight() {
        return height.get();
    }
 
}

推荐答案

至少从JavaFX 15开始,thenotherwise绑定至少得到了评估 1 .我不确定是否应该急切地对它们进行评估,但确实如此.因此,尽管您可能以panelProperty.isNotNull()为条件,但无论如何,使用createObjectBinding创建的绑定仍会尝试计算该值.在这种情况下,最好放弃使用Bindings.when并直接在自定义绑定中自己实现逻辑:

The then and otherwise bindings are, at least as of JavaFX 15, evaluated eagerly1. I'm not sure if they're supposed1 to be evaluated eagerly, but they are. So while you may be conditioning upon panelProperty.isNotNull() the binding you create with createObjectBinding still attempts to compute the value no matter what. In this case, it's probably better to forgo the use of Bindings.when and simply implement the logic yourself in a custom binding:

private ObjectBinding<Dimension2D> initPanelSizeBinding(ObjectProperty<Panel> panelProperty) {
  DoubleBinding width = Bindings.selectDouble(panelProperty, "width");
  DoubleBinding height = Bindings.selectDouble(panelProperty, "height");
  return Bindings.createObjectBinding(
      () -> {
        Panel panel = panelProperty.get();
        if (panel == null) {
          return new Dimension2D(150.0, 150.0);
        }
        return new Dimension2D(width.get(), height.get());
      },
      panelProperty,
      width,
      height);
}


1. 更多地研究它,看来条件绑定确实试图变得懒惰.不幸的是,条件绑定的行为向then可观察者添加了侦听器,导致所说的可观察者得到验证(因此急切地进行了评估).这是ExpressionHelper的实现方式的结果,该实现调用observable.getValue()对其进行验证.我不明白为什么代码会执行此操作,特别是考虑到添加的InvalidationListener应该是惰性侦听器.


1. Looking into it more, it appears the conditional binding does try to be lazy. Unfortunately, the act of the condition binding adding a listener to the then observable causes said observable to be validated (and thus eagerly evaluated). This is a consequence of how ExpressionHelper is implemented, which calls observable.getValue() to validate it. I don't understand why the code does this, especially considering it's an InvalidationListener being added which is supposed to be the lazy listener.

这篇关于有条件的绑定不能按预期方式工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆