Gre*_*bra 5 javafx tableview propertychangelistener javafx-8
一个相对的Java新手问题.
我有一个带有提取器的TableView,并ListChangeListener添加到底层的ObservableList中.
如果我StringProperty在数据模型中有一个列,那么如果我双击单元格然后按Enter键而不做任何更改,则更改侦听器不会检测到更改.非常好.
但是,如果我将列定义为ObjectProperty<String>并双击然后按Enter,则更改侦听器始终会检测到更改,即使没有进行更改也是如此.
为什么会这样?改变听众的观点ObjectProperty<String>和StringProperty改变之间的区别是什么?
我已经阅读了SimpleStringProperty和StringProperty以及JavaFX SimpleObjectProperty <T>与SimpleTProperty之间的差异,并认为我理解这些差异.但是我不明白为什么改变监听器给TProperty/ SimpleTProperty和提供不同的结果ObjectProperty<T>.
如果它有帮助,这是一个MVCE我的有点荒谬的情况.我实际上是想让一个改变监听器工作BigDecimal和LocalDate列,并且已经坚持了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);
}
}
Run Code Online (Sandbox Code Playgroud)
StringPropertyBase通过查看和ObjectPropertyBase\xe2\x80\x94的源代码可以看出区别,特别是它们的set方法。
StringPropertyBase@Override\npublic void set(String newValue) {\n if (isBound()) {\n throw new java.lang.RuntimeException((getBean() != null && getName() != null ?\n getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set.");\n }\n if ((value == null)? newValue != null : !value.equals(newValue)) {\n value = newValue;\n markInvalid();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\nObjectPropertyBase@Override\npublic void set(T newValue) {\n if (isBound()) {\n throw new java.lang.RuntimeException((getBean() != null && getName() != null ?\n getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set.");\n }\n if (value != newValue) {\n value = newValue;\n markInvalid();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n注意到他们检查新值是否等于旧值的方式有何不同吗?该类StringPropertyBase通过 using 进行检查Object.equals,而该类则ObjectPropertyBase使用引用相等性 ( ==/ !=)。
我无法确切回答为什么存在这种差异,但我可以大胆猜测:AnObjectProperty可以容纳任何东西,因此有可能Object.equals很昂贵;例如当使用List或 时Set。当编码时StringPropertyBase,我猜他们认为潜力不存在,String平等的语义更重要,或者两者兼而有之。他们这样做可能有更多/更好的原因,但由于我没有参与开发,所以我不知道它们。
有趣的是,如果您查看它们如何处理侦听器\n( com.sun.javafx.binding.ExpressionHelper),您会发现它们使用 来检查相等性Object.equals。仅当当前有ChangeListeners 注册\xe2\x80\x94 时才会发生此相等性检查,可能是为了在没有ChangeListeners 时支持延迟求值。
如果新值和旧值相同,则不会通知equalss 。然而,这并不能阻止s收到通知。因此,您将触发更新更改,因为该机制基于s 而不是s。ChangeListenerInvalidationListenerObservableListInvalidationListenerChangeListener
这是相关的源代码:
\n\nExpressionHelper$Generic.fireValueChangedEvent@Override\nprotected void fireValueChangedEvent() {\n final InvalidationListener[] curInvalidationList = invalidationListeners;\n final int curInvalidationSize = invalidationSize;\n final ChangeListener<? super T>[] curChangeList = changeListeners;\n final int curChangeSize = changeSize;\n\n try {\n locked = true;\n for (int i = 0; i < curInvalidationSize; i++) {\n try {\n curInvalidationList[i].invalidated(observable);\n } catch (Exception e) {\n Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);\n }\n }\n if (curChangeSize > 0) {\n final T oldValue = currentValue;\n currentValue = observable.getValue();\n final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);\n if (changed) {\n for (int i = 0; i < curChangeSize; i++) {\n try {\n curChangeList[i].changed(observable, oldValue, currentValue);\n } catch (Exception e) {\n Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);\n }\n }\n }\n }\n } finally {\n locked = false;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n您可以在以下代码中看到此行为:
\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\n\npublic class Main {\n\n public static void main(String[] args) {\n ObjectProperty<String> property = new SimpleObjectProperty<>("Hello, World!");\n property.addListener(obs -> System.out.printf("Property invalidated: %s%n", property.get()));\n property.addListener((obs, ov, nv) -> System.out.printf("Property changed: %s -> %s%n", ov, nv));\n property.get(); // ensure valid\n\n property.set(new String("Hello, World!")); // must not use interned String\n property.set("Goodbye, World!");\n }\n\n}\nRun Code Online (Sandbox Code Playgroud)\n\n输出:
\n\n@Override\npublic void set(String newValue) {\n if (isBound()) {\n throw new java.lang.RuntimeException((getBean() != null && getName() != null ?\n getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set.");\n }\n if ((value == null)? newValue != null : !value.equals(newValue)) {\n value = newValue;\n markInvalid();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
142 次 |
| 最近记录: |