JavaFX:在不破坏选择模型的情况下在 ListView 中显示图像

JavaFX: Display image in ListView without breaking selection model

问题

我想显示一个包含文本和图像的列表。我可以做到,但是 selection 模型很时髦。当我用鼠标 select 列表中的一个项目时,似乎整个列表视图元素都被 select 编辑了。当我使用箭头键时,selection 模型工作正常。

我的代码

在我的控制器中有 ObservableList<Game> gameList。游戏 class 看起来像这样:

public class Game {
    private String name;
    private Image image;
}

Stack Overflow 上旧解决方案的示例

在搜索有关如何显示图像和名称的解决方案时,我发现许多使用 setCellFactory 方法的 Stack Overflow 解决方案,如下面的代码片段:

listView.setCellFactory(param -> new ListCell<>() {
            private final ImageView imageView = new ImageView();
            @Override
            public void updateItem(String item, boolean empty) {
                if (empty) {
                    setText(null);
                    setGraphic(null);
                } else {
                    imageView.setImage(/*Some image*/);
                    setText(game.getName());
                    setGraphic(imageView);
                }
            }
        });

我的解决方案尝试

但是,我想要显示的图像存储在我的 ObservableList 的 Game 对象中。据我了解,上面的 String item 参数是 Game 对象 toString 方法,但我想在制作自定义 ListCell 时访问整个 Game 对象。我试图改变该解决方案以访问整个 Game 对象。这是我的代码目前的样子:

public class MyController implements Initializable {
    @FXML
    public ListView<Game> listView;

    public ObservableList<Game> gameList;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        gameList = FXCollections.observableList(/*List of games*/);
        listView.setItems(gameList);
        listView.setCellFactory(param -> new ListCell<>() {
            private final ImageView imageView = new ImageView();
            @Override
            public void updateItem(Game game, boolean empty) {
                if (empty) {
                    setText(null);
                    setGraphic(null);
                } else {
                    imageView.setImage(game.getImage());
                    setText(game.getName());
                    setGraphic(imageView);
                }
            }
        });
    }
}

结果

使用上面的代码,我可以在我的 ListView 中显示每个游戏及其名称。

我正在尝试解决的问题

列表显示完全符合我的要求,但 selection 模型似乎已损坏。 我使用 listView.getSelectionModel().getSelectedItem(); 来获取 selected 游戏。当我用鼠标select一个项目时,上面的方法returns无效。这是当我左键单击列表中的“其他游戏”项目时的样子:

但是,我可以使用箭头键 select 列表中我想要的任何项目。当我这样做时,我的 ObservableList 中的 selected 游戏被返回。

有人知道我该如何解决这个问题吗?

默认的 ListCell.updateItem(...) 方法处理选择等。所以你需要一定要调用它。

来自documentation

It is very important that subclasses of Cell override the updateItem method properly ... Note in this code sample two important points:

  1. We call the super.updateItem(T, boolean) method. If this is not done, the item and empty properties are not correctly set, and you are likely to end up with graphical issues.
  2. ...

所以你需要:

listView.setCellFactory(param -> new ListCell<>() {
    private final ImageView imageView = new ImageView();
    @Override
    public void updateItem(Game game, boolean empty) {

       // call default implementation:
       super.updateItem(item, empty);

       if (empty) {
            setText(null);
            setGraphic(null);
        } else {
            imageView.setImage(game.getImage());
            setText(game.getName());
            setGraphic(imageView);
        }
    }
});