一个相对的Java新手问题。
我有一个带有提取器的TableView和一个添加到基础ObservableList中的ListChangeListener
。
如果我在数据模型中有一个StringProperty
列,如果我双击单元格,然后在不进行任何更改的情况下单击ENTER,则更改侦听器不会检测到更改。那很好。
但是,如果我将列定义为ObjectProperty
为什么会这样?
ObjectProperty之间有什么区别
我已经读过SimpleStringProperty和StringProperty以及JavaFX SimpleObject Property之间的区别
如果有帮助的话,这是我有点荒谬的案例的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);
}
}
匿名用户
通过查看StringProperty tyBase
和ObjectProperty 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
),你会发现它们使用对象检查是否相等。等于
。只有当当前注册了ChangeListener
s,以支持延迟求值,而没有ChangeListener
s时,才会进行此相等性检查。
如果新值和旧值相等
则不会通知ChangeListener
s。然而,这并不能阻止invalizationlistener
s收到通知。因此,您的observateList
将触发更新更改,因为该机制基于invalizationListener
s,而不是ChangeListener
s。
以下是相关的源代码:
@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