JavaFXListView自定义单元格工厂为可观察列表中的第一个条目创建两个对象
我正在学习列表视图的自定义单元格工厂,我有一个问题。问题是,对于items的第一个条目,单元格工厂创建了两个单元格,这只是第一个条目,我不知道为什么,可能是我做错了什么。如果你指出我做错了什么,我将不胜感激 这是我的主要课程:JavaFXListView自定义单元格工厂为可观察列表中的第一个条目创建两个对象,listview,javafx,Listview,Javafx,我正在学习列表视图的自定义单元格工厂,我有一个问题。问题是,对于items的第一个条目,单元格工厂创建了两个单元格,这只是第一个条目,我不知道为什么,可能是我做错了什么。如果你指出我做错了什么,我将不胜感激 这是我的主要课程: public class TestMain3 extends Application { private ObjectProperty<String> field; public TestMain3() { } @Override public void
public class TestMain3 extends Application {
private ObjectProperty<String> field;
public TestMain3() {
}
@Override
public void start(Stage stage) throws Exception {
ObservableList<TestMain3> observableList = FXCollections.observableArrayList();
observableList.addAll(new TestMain3("w"), new TestMain3("y"), new TestMain3("u"), new TestMain3("a"), new TestMain3("a"), new TestMain3("a"));
System.out.println(observableList.toString() + " observable list");
ObservableList<TestCell> testCells = FXCollections.observableArrayList();
ListView<TestMain3> listView = new ListView<>();
listView.setCellFactory(new Callback<ListView<TestMain3>, ListCell<TestMain3>>() {
@Override
public ListCell<TestMain3> call(ListView<TestMain3> param) {
TestCell testCell = new TestCell();
testCells.add(testCell);
return testCell;
}
});
listView.setItems(observableList);
VBox vBox = new VBox();
vBox.getChildren().add(listView);
Button button = new Button("ADD");
button.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
}
});
button.setAlignment(Pos.CENTER_RIGHT);
vBox.getChildren().add(button);
Scene scene = new Scene(vBox, 600, 600);
stage.setScene(scene);
stage.show();
System.out.println(testCells.toString());
}
public TestMain3(String t) {
field = new SimpleObjectProperty<>();
field.set(t);
}
public static void main(String[] args) {
launch(args);
}
public String getField() {
return field.get();
}
public ObjectProperty fieldProperty() {
return field;
}
public void setField(String field) {
this.field.set(field);
}
@Override
public boolean equals(Object testMain3) {
if (testMain3 == null) {
return false;
}
TestMain3 testMain31 = (TestMain3) testMain3;
return getField().equalsIgnoreCase(testMain31.getField());
}
@Override
public String toString() {
return field.getValue().toString();
}
}
正如你们在我的可观察列表中看到的,我只有一个W,然而listview创建了两个W,即使它没有显示在listview中,它也会产生一些问题
更新
当列表被重用时,我确实会删除侦听器
@Override
protected void updateItem(Contact contact, boolean empty) {
super.updateItem(contact, empty);
if (empty || contact == null) {
setText(null);
setGraphic(null);
setContextMenu(null);
} else {
registerListeners(contact);
.....
}
}
registerListeners方法
private void registerListeners(Contact contact) {
if (person != null) {
person.getChat().newMessagesCountProperty().
removeListener(newMessagesChangeListener);
person.getChat().getMessages().
removeListener(chatMessagesListChangeListener);
onlineIconView.visibleProperty().unbind();
}
person = contact;
onlineIconView.visibleProperty().bind(contact.onlineProperty());
person.getChat().newMessagesCountProperty().
addListener(newMessagesChangeListener);
person.getChat().getMessages().
addListener(chatMessagesListChangeListener);
}
我正在创建一个聊天应用程序,问题是当listview中的第一个人收到新消息时,其单元格应该更新,显示新消息的图标和新消息的数量,但是其侦听器会被调用两次,因此看起来用户收到了两条新消息,而实际上他只收到了一条。
在第一次接触之后,接触没有问题
更新2
现在,在
updateItem()
中,如果它是第一个非空单元格,我只返回nothing,因此它解决了我的问题。我认为这是一个bug。这里没有什么问题
单元格工厂及其创建的ListCell
的唯一职责是创建足够的单元格来填充ListView
,并在单元格显示的项目发生更改时调用updateItem(…)
。无法保证实际创建了多少个单元格,以及对每个单元格调用了多少次updateItem(…)
由于您观察到只有一个单元格显示,且其文本为“w”
,因此单元格工厂和单元格工作正常
您看到使用字段为“w”
的参数调用updateItem(…)
两次,这只是您正在使用的JavaFX版本中实现单元工厂机制方式的一个副作用。您可能会在其他版本中观察到不同的行为
关于这一点,您真正需要知道的只有:
- 不要假设特定单元格上调用了多少次
。特别是,列表视图中的项目与显示的单元格之间没有一对一的对应关系,因此将多次调用updateItem(…)
以重用单元格(例如在滚动期间)updateItem(…)
- 很少创建单元格(通常,仅当第一次显示
时,也可能是当ListView
高度增加时)ListView
可能会被频繁调用(例如,当用户在updateItem(…)
列表视图中滚动时)。因此,不要在
updateItem(…)
updateItem(…)
),但是从输出中可以看到,它创建了两个不同的单元,并使用相同的参数(TestMain3(“w”)调用了这两个单元updateItem()
)。所以我有7个单元格,它们的值不是空的,而在可观察列表中我只有6个是的,我看到了。也许这很奇怪,但它并没有违反任何声明的功能。实际问题是什么?在updateItem(…)
的开始,我将一些监听器绑定到我的对象(当然,如果它不为null),基于这些监听器,它应该更改单元格外观(ui),在我的情况下,我认为前两个单元格重叠或类似的内容,并且我没有得到想要的ui更改。如果你不删除监听器,你将有一个bug,因为在重复使用单元后,单元将监听多个项目。发布一些代码。你可以使用带有。这样,当您指定的任何属性发生更改时,都会调用updateItem(…)
方法,您根本不需要管理侦听器。
@Override
protected void updateItem(Contact contact, boolean empty) {
super.updateItem(contact, empty);
if (empty || contact == null) {
setText(null);
setGraphic(null);
setContextMenu(null);
} else {
registerListeners(contact);
.....
}
}
private void registerListeners(Contact contact) {
if (person != null) {
person.getChat().newMessagesCountProperty().
removeListener(newMessagesChangeListener);
person.getChat().getMessages().
removeListener(chatMessagesListChangeListener);
onlineIconView.visibleProperty().unbind();
}
person = contact;
onlineIconView.visibleProperty().bind(contact.onlineProperty());
person.getChat().newMessagesCountProperty().
addListener(newMessagesChangeListener);
person.getChat().getMessages().
addListener(chatMessagesListChangeListener);
}