Javafx ListView Custom Cell Factory为可观察列表中的第一个条目创建两个对象

时间:2015-08-13 08:54:24

标签: listview javafx

我正在学习列表视图的自定义单元工厂,我有一个问题。问题是,对于第一个条目单元格工厂创建两个单元格,它只是第一个条目,我不知道为什么,可能是我做错了什么。如果你指出我做错了什么,我将非常感激。

这是我的主要课程:

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();
}
}

这是我的自定义ListCell:

public class TestCell extends ListCell<TestMain3> {
static int counter = 0;
private String text;

public TestCell() {
    counter++;
    System.out.println(counter);
}

@Override
protected void updateItem(TestMain3 testMain3, boolean empty) {
    super.updateItem(testMain3, empty);
    if (empty || testMain3 == null) {
        setText(null);
        setGraphic(null);
        setContextMenu(null);
    } else {
        text = testMain3.getField();
        System.out.println(testMain3 + " not null");
        setText(testMain3.getField());
    }
}

public String getTextString() {
    return text;
}

public void setTextString(String text) {
    this.text = text;
}

@Override
public String toString() {
    return text + " test cell";
}
}

这是我的输出:

[w, y, u, a, a, a] observable list
1
w not null
2
w not null
3
y not null
4
u not null
5
a not null
6
a not null
7
a not null
8
9
10
11
12
13
14
15
16
17
18
19
[w test cell, w test cell, y test cell, u test cell, a test cell, a test           cell, a test cell, null test cell, null test cell, null test cell, null test   cell, null test cell, null test cell, null test cell, null test cell, null test cell, null test cell, null test cell, null test cell]

因为你可以在我的可观察列表中看到我只有一个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中的第一个人获得新消息时,其单元格应该更新,显示新消息的图标和新消息的数量,但是它的侦听器被调用两次因此看起来像用户有两条新消息,而事实上他只有一条消息。 第一个接触后的联系人没有问题

UPDATE2 现在在updateItem()如果它是第一个非空单元格,我只是不做任何事情,所以对我来说它解决了我的问题。我认为这是一个错误。

1 个答案:

答案 0 :(得分:0)

这里没有错。

单元工厂及其创建的ListCell的唯一责任是创建足够的单元格来填充ListView,并在单元格显示的项目更改时调用updateItem(...) 。不保证实际创建了多少个单元格以及在每个单元格上调用了多少次updateItem(...)

由于您只观察到一个单元格显示为"w"作为其文本,因此单元格工厂和单元格正常工作。

您看到updateItem(...)使用其字段为"w"两次的参数调用的事实只是在您正在使用的JavaFX版本中实现单元工厂机制的方式的副作用。您可能会在其他版本中观察到不同的行为。

您真正需要了解的唯一事项是:

  • 不要假设有关在特定单元格上调用updateItem(...)次的具体内容。特别是,列表视图中的项目与显示的单元格之间没有一对一的对应关系,因此将多次调用updateItem(...)以重复使用单元格(例如,在滚动期间)。
  • 很少创建单元格(通常,仅在首次显示ListView时,但也可能在ListView高度增加时)
  • 可以非常频繁地调用
  • updateItem(...)(例如,当用户在ListView中滚动时)。因此,请勿在{{1​​}}
  • 中执行任何性能密集型工作