JavaFXListView没有';删除自定义ListCell时无法正确刷新

JavaFXListView没有';删除自定义ListCell时无法正确刷新,listview,javafx,Listview,Javafx,我正在为我的测验应用程序编写一个编辑器 我有一个自定义的ListCell类,它看起来像这样: public class QuestionListCell extends ListCell<Question> { private static final Logger LOGGER = Logger.getLogger(QuizLoader.class.getName()); @FXML HBox cellBox; @FXML Label title; @FXML GridPane

我正在为我的测验应用程序编写一个编辑器

我有一个自定义的
ListCell
类,它看起来像这样:

public class QuestionListCell extends ListCell<Question> {

private static final Logger LOGGER = Logger.getLogger(QuizLoader.class.getName());

@FXML HBox cellBox;
@FXML Label title;
@FXML GridPane answersGrid;

private FXMLLoader loader;

@Override
protected void updateItem(Question item, boolean empty) {
    super.updateItem(item, empty);
    if (empty || item == null) {
        setText(null);
        setGraphic(null);
    }
    else {
        if (loader == null) {
            loader = new FXMLLoader(getClass().getResource(QUESTION_LIST_CELL_FXML));
            loader.setController(this);
            try {
                loader.load();
            }
            catch (IOException e) {
                LOGGER.log(Level.SEVERE, e.getMessage(), e);
            }
        }
        addContent(item);
    }
}

private void addContent(Question question) {
    title.textProperty().bind(question.getQuestionTextProperty());

    int answersSize = question.getAnswers().size();
    for (int i = 0; i < answersSize; i++) {
        int rowIndex = i / 2;
        int colIndex = i % 2 == 0 ? 0 : 1;

        Label answerLabel = new Label();
        answerLabel.textProperty().bind(question.getAnswers().get(i).getAnswerTextProperty());
        answerLabel.prefWidthProperty().bind(Bindings.divide(answersGrid.widthProperty(), 2));
        answersGrid.add(answerLabel, colIndex, rowIndex);
    }

    final NumberBinding cellBoxWidth = Bindings.subtract(getListView().widthProperty(), 20);
    cellBox.prefWidthProperty().bind(cellBoxWidth);
    answersGrid.prefWidthProperty().bind(cellBoxWidth);
    setGraphic(cellBox);
}
@FXML
private void onAddButtonClicked() {
    LOGGER.log(Level.INFO, "addButton clicked.");
    // quiz.getQuestions().add(quiz.getQuestions().get(0));
}

@FXML
private void onDeleteButtonClicked() {
    LOGGER.log(Level.INFO, "deleteButton clicked.");

    int index = listView.getSelectionModel().getSelectedIndex();
    if (index >= 0) {
        quiz.getQuestions().remove(index);
    }
}

private void setUpQuestionsListView() {
    listView.setCellFactory(listView -> new QuestionListCell());
    listView.itemsProperty().bindBidirectional(quiz.getQuestionsProperty());
}
到目前为止,ListView看起来是这样的:

public class QuestionListCell extends ListCell<Question> {

private static final Logger LOGGER = Logger.getLogger(QuizLoader.class.getName());

@FXML HBox cellBox;
@FXML Label title;
@FXML GridPane answersGrid;

private FXMLLoader loader;

@Override
protected void updateItem(Question item, boolean empty) {
    super.updateItem(item, empty);
    if (empty || item == null) {
        setText(null);
        setGraphic(null);
    }
    else {
        if (loader == null) {
            loader = new FXMLLoader(getClass().getResource(QUESTION_LIST_CELL_FXML));
            loader.setController(this);
            try {
                loader.load();
            }
            catch (IOException e) {
                LOGGER.log(Level.SEVERE, e.getMessage(), e);
            }
        }
        addContent(item);
    }
}

private void addContent(Question question) {
    title.textProperty().bind(question.getQuestionTextProperty());

    int answersSize = question.getAnswers().size();
    for (int i = 0; i < answersSize; i++) {
        int rowIndex = i / 2;
        int colIndex = i % 2 == 0 ? 0 : 1;

        Label answerLabel = new Label();
        answerLabel.textProperty().bind(question.getAnswers().get(i).getAnswerTextProperty());
        answerLabel.prefWidthProperty().bind(Bindings.divide(answersGrid.widthProperty(), 2));
        answersGrid.add(answerLabel, colIndex, rowIndex);
    }

    final NumberBinding cellBoxWidth = Bindings.subtract(getListView().widthProperty(), 20);
    cellBox.prefWidthProperty().bind(cellBoxWidth);
    answersGrid.prefWidthProperty().bind(cellBoxWidth);
    setGraphic(cellBox);
}
@FXML
private void onAddButtonClicked() {
    LOGGER.log(Level.INFO, "addButton clicked.");
    // quiz.getQuestions().add(quiz.getQuestions().get(0));
}

@FXML
private void onDeleteButtonClicked() {
    LOGGER.log(Level.INFO, "deleteButton clicked.");

    int index = listView.getSelectionModel().getSelectedIndex();
    if (index >= 0) {
        quiz.getQuestions().remove(index);
    }
}

private void setUpQuestionsListView() {
    listView.setCellFactory(listView -> new QuestionListCell());
    listView.itemsProperty().bindBidirectional(quiz.getQuestionsProperty());
}

但是,当我删除第一项(并且仅删除第一项)时,第二项的GridPane似乎覆盖在第一项上

就我所知,如果ListView中有更多的元素,就不会发生这种情况,只有当只剩下两个元素并且我正在删除第一个元素时才会发生这种情况。我做错了什么?

添加行

answersGrid.getChildren().clear() ;
addContent()
的开头(或者,如果您愿意,在调用
addContent()
之前的
updateItem()
中)

问题是每个单元格只加载一次FXML(这是一件好事);如果(当)该单元格随后被重用,将再次调用
updateItem()
,而不更改
answersGrid
引用,并且您将向网格添加更多子元素,而不删除原始子元素。因此,当重复使用单元格时(例如,通过滚动,或当删除一个项目并将其单元格用于另一个项目时),多个节点将添加到网格中的相同位置


如上所述,从网格中删除现有标签将解决此问题。

谢谢,这很有效!我以前也做过类似的事情,只是我尝试了answersGrid.getChildren.removeAll(),我错误地认为这会删除所有的孩子。但现在我明白了,这种方法是用来移除特定的子对象的。LPT:始终阅读Javadoc。