当前位置: 首页 > 知识库问答 >
问题:

为什么TableView的更改监听器在JavaFX8中为ObjectProperty与TProperty列提供不同的结果?

廉飞捷
2023-03-14

一个相对的Java新手问题。

我有一个带有提取器的TableView和一个添加到基础ObservableList中的ListChangeListener

如果我在数据模型中有一个StringProperty列,如果我双击单元格,然后在不进行任何更改的情况下单击ENTER,则更改侦听器不会检测到更改。那很好。

但是,如果我将列定义为ObjectProperty

为什么会这样?ObjectProperty之间有什么区别

我已经读过SimpleStringProperty和StringProperty以及JavaFX SimpleObject Property之间的区别

如果有帮助的话,这是我有点荒谬的案例的MVCE。实际上,我正在尝试让一个更改监听器为BigDecimalLocalDate列工作,并且已经在上面停留了5天。如果我能理解为什么更改监听器给出不同的结果,我也许能够让我的代码工作。

我正在使用JavaFX8(JDK1.8.0_181)、NetBeans 8.2和Scene Builder 8.3。

package test17;

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.Observable;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.converter.DefaultStringConverter;

public class Test17 extends Application {

    private Parent createContent() {

        ObservableList<TestModel> olTestModel = FXCollections.observableArrayList(testmodel -> new Observable[] {
                testmodel.strProperty(),
                testmodel.strObjectProperty()
        });

        olTestModel.add(new TestModel("A", "a"));
        olTestModel.add(new TestModel("B", "b"));

        olTestModel.addListener((ListChangeListener.Change<? extends TestModel > c) -> {
            while (c.next()) {
                if (c.wasUpdated()) {
                    System.out.println("===> wasUpdated() triggered");
                }
            }
        });

        TableView<TestModel> table = new TableView<>();

        TableColumn<TestModel, String> strCol = new TableColumn<>("strCol");
        strCol.setCellValueFactory(cellData -> cellData.getValue().strProperty());
        strCol.setCellFactory(TextFieldTableCell.forTableColumn(new DefaultStringConverter()));
        strCol.setEditable(true);
        strCol.setPrefWidth(100);
        strCol.setOnEditCommit((CellEditEvent<TestModel, String> t) -> {
                ((TestModel) t.getTableView().getItems().get(
                        t.getTablePosition().getRow())
                        ).setStr(t.getNewValue());
        });

        TableColumn<TestModel, String> strObjectCol = new TableColumn<>("strObjectCol");
        strObjectCol.setCellValueFactory(cellData -> cellData.getValue().strObjectProperty());
        strObjectCol.setCellFactory(TextFieldTableCell.forTableColumn(new DefaultStringConverter()));
        strObjectCol.setEditable(true);
        strObjectCol.setPrefWidth(100);
        strObjectCol.setOnEditCommit((CellEditEvent<TestModel, String> t) -> {
            ((TestModel) t.getTableView().getItems().get(
                    t.getTablePosition().getRow())
                    ).setStrObject(t.getNewValue());
        });

        table.getColumns().addAll(strCol, strObjectCol);
        table.setItems(olTestModel);
        table.getSelectionModel().setCellSelectionEnabled(true);
        table.setEditable(true);

        BorderPane content = new BorderPane(table);
        return content;
    }

    public class TestModel {

        private StringProperty str;
        private ObjectProperty<String> strObject;

        public TestModel(
            String str,
            String strObject
        ) {
            this.str = new SimpleStringProperty(str);
            this.strObject = new SimpleObjectProperty(strObject);
        }

        public String getStr() {
            return this.str.get();
        }

        public void setStr(String str) {
            this.str.set(str);
        }

        public StringProperty strProperty() {
            return this.str;
        }

        public String getStrObject() {
            return this.strObject.get();
        }

        public void setStrObject(String strObject) {
            this.strObject.set(strObject);
        }

        public ObjectProperty<String> strObjectProperty() {
            return this.strObject;
        }

    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.setTitle("Test");
        stage.setWidth(350);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

共有1个答案

益锦程
2023-03-14
匿名用户

通过查看StringProperty tyBaseObjectProperty tyBase的源代码可以看出差异-具体地说,他们的set方法。

@Override
public void set(String newValue) {
    if (isBound()) {
        throw new java.lang.RuntimeException((getBean() != null && getName() != null ?
                getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set.");
    }
    if ((value == null)? newValue != null : !value.equals(newValue)) {
        value = newValue;
        markInvalid();
    }
}
@Override
public void set(T newValue) {
    if (isBound()) {
        throw new java.lang.RuntimeException((getBean() != null && getName() != null ?
                getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set.");
    }
    if (value != newValue) {
        value = newValue;
        markInvalid();
    }
}

注意到他们检查新值是否等于旧值的不同之处了吗?类通过使用Object.equals进行检查,而类使用引用相等(code>==/code>!=)。

我不能肯定地回答为什么会存在这种差异,但我可以大胆猜测:ObjectProperty可以容纳任何东西,因此存在对象的可能性。相当于昂贵;例如使用列表设置时。当编写StringPropertyBase时,我猜他们认为潜力不存在,认为String相等的语义更重要,或者两者都重要。他们这么做可能有更多/更好的原因,但由于我没有参与开发,我不知道他们。

有趣的是,如果你看看它们如何处理侦听器(com.sun.javafx.binding.ExpressionHelper),你会发现它们使用对象检查是否相等。等于。只有当当前注册了ChangeListeners,以支持延迟求值,而没有ChangeListeners时,才会进行此相等性检查。

如果新值和旧值相等则不会通知ChangeListeners。然而,这并不能阻止invalizationlisteners收到通知。因此,您的observateList将触发更新更改,因为该机制基于invalizationListeners,而不是ChangeListeners。

以下是相关的源代码

@Override
protected void fireValueChangedEvent() {
    final InvalidationListener[] curInvalidationList = invalidationListeners;
    final int curInvalidationSize = invalidationSize;
    final ChangeListener<? super T>[] curChangeList = changeListeners;
    final int curChangeSize = changeSize;

    try {
        locked = true;
        for (int i = 0; i < curInvalidationSize; i++) {
            try {
                curInvalidationList[i].invalidated(observable);
            } catch (Exception e) {
                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
            }
        }
        if (curChangeSize > 0) {
            final T oldValue = currentValue;
            currentValue = observable.getValue();
            final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);
            if (changed) {
                for (int i = 0; i < curChangeSize; i++) {
                    try {
                        curChangeList[i].changed(observable, oldValue, currentValue);
                    } catch (Exception e) {
                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                    }
                }
            }
        }
    } finally {
        locked = false;
    }
}

您可以在以下代码中看到这种行为:

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;

public class Main {

  public static void main(String[] args) {
    ObjectProperty<String> property = new SimpleObjectProperty<>("Hello, World!");
    property.addListener(obs -> System.out.printf("Property invalidated: %s%n", property.get()));
    property.addListener((obs, ov, nv) -> System.out.printf("Property changed: %s -> %s%n", ov, nv));
    property.get(); // ensure valid

    property.set(new String("Hello, World!")); // must not use interned String
    property.set("Goodbye, World!");
  }

}

输出:

Property invalidated: Hello, World!
Property invalidated: Goodbye, World!
Property changed: Hello, World! -> Goodbye, World!

 类似资料:
  • 问题内容: 我一直在类设计方面遇到问题,直到我发现了可观察的(使用观察者设计模式),因此使用它创建了一个小型应用程序,从而解决了我的问题。我为使用一个好的原则解决问题感到高​​兴和自豪。 为什么建议张贴者不要使用可观察的,而要告诉他们使用propertychangelistenr?使用observable是否有任何问题? 问候 问题答案: 观察者和侦听器模式非常相似。但是观察者有一个弱点:所有可观

  • 我做了一个程序,动态地从一个面板获取数据,但是我的代码需要用户点击输入按钮来更新数据。有没有一个更改监听器或其他监听器可以在任何时候更新Jtext field中的数据?谢谢!

  • 当我跑的时候。使用CPLEX的NET 4应用程序,我在不同的机器上得到不同的输出。在我的开发机器上,CPLEX输出一个结果(异常并卡在某个大值上),在所有其他机器上,结果都可以。 首先,我认为它与操作系统有关,因为我的开发机器上同时有视窗7 x64和视窗8 x64,所以我尝试在两个系统上运行应用程序。结果是一样的——有缺陷。 然后我试着在两台不同的台式机上运行,效果很好。我甚至在虚拟机内部进行了尝

  • 问题内容: 为什么更改总和顺序会返回不同的结果? = = 双方的Java和JavaScript的返回相同的结果。 我知道,由于以二进制表示浮点数的方式,某些有理数( 例如1/3-0.333333 … )无法精确表示。 为什么简单地更改元素的顺序会影响结果? 问题答案: 也许这个问题很愚蠢,但是为什么仅仅改变元素的顺序会影响结果呢? 它将根据值的大小更改四舍五入的点。作为示例 _样的_事情,我们所看

  • 我试图找出如何检测一些对象属性的变化。所以一开始我就意识到该怎么做,而且很管用。我不知道是否有更好的方法(如果你们知道是否有更好的方法,请告诉我)。 问题是,我还有另一个问题要解决,我不想检测变化,我只想获得属性的新设置值。因此,我知道使用javafx.beans.value.ChangeListener接口可以检测某个属性的每一个更改,这就是我的问题所在,因为我创建了一个实现ChangeList

  • 你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用v-on有几个好处: 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。 因为你无须在 JavaScrip