我正在尝试制作一个ListView,其中每个单元格由一个Label和一个Button组成。我想让鼠标悬停在单元格上方时显示按钮,而当鼠标悬停时按钮消失。为此,我在ListCell对象上使用了setOnMouseEntered()方法,但是Button仅出现在第一个单元格(添加到ObservableList的最新项)中

我的自定义ListCell类:

public class SubscribedTopicListCell extends ListCell<String> {

    private final Label lSubscribedTopic = new Label();
    private final Button btnUnsubscribe = new Button();
    private static final ImageView ivBtnGraphic = new ImageView(new Image("resources/images/cross.png"));

    @Override
    protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        setGraphic(null);
        if (!empty && item != null) {
            lSubscribedTopic.setText(item);
            btnUnsubscribe.setVisible(false);
            btnUnsubscribe.setGraphic(ivBtnGraphic);
            btnUnsubscribe.setBackground(Background.EMPTY);
            btnUnsubscribe.setOnAction(e ->
                MqttConnection.getInstance().unsubscribe(item)
            );
            this.setOnMouseEntered(e ->
                btnUnsubscribe.setVisible(true)
            );
            this.setOnMouseExited(e ->
                btnUnsubscribe.setVisible(false)
            );
            GridPane listCellPane = new GridPane();
            listCellPane.add(lSubscribedTopic, 0, 0);
            listCellPane.add(btnUnsubscribe, 1, 0);
            ColumnConstraints col0 = new ColumnConstraints();
            col0.setHalignment(HPos.LEFT);
            ColumnConstraints col1 = new ColumnConstraints();
            col1.setHalignment(HPos.RIGHT);
            col1.setHgrow(Priority.ALWAYS);
            listCellPane.getColumnConstraints().addAll(col0, col1);
            setGraphic(listCellPane);
        }
    }
}


如何使它适用于每个单元?

最佳答案

因为您已将ImageView设为静态,所以只有一个单元格可以具有一个按钮。 ImageViewNode,每个节点在场景图中只能出现一次。通过将ImageView设置为静态,可以尝试将相同的ImageView强制插入场景图中的多个位置。当您在按钮上调用setGraphic(...)时,将有效地将图像视图从上一个按钮中删除,以实现在场景图中只能使用一次的规则。 (我怀疑所有按钮都确实存在,但是只有一个按钮具有图形:由于没有文本且没有背景,其他按钮是完全不可见的,可能尺寸为零。)

解决方法是使ImageView成为实例变量。请注意,Image不是节点,并且多个ImageView可以共享同一个Image,因此可以通过将Image设置为静态来避免在内存中复制图像数据。

最后,虽然它不会真正改变功能,但是每次调用updateItem()时重新注册侦听器都没有任何意义。您只需在构造函数中执行一次即可。对于布局类似:

public class SubscribedTopicListCell extends ListCell<String> {

    private final Label lSubscribedTopic = new Label();
    private final Button btnUnsubscribe = new Button();

    private static final Image image = new Image("resources/images/cross.png");

    private final ImageView ivBtnGraphic = new ImageView(image);

    private final GridPane listCellPane = new GridPane();

    public SubscribedTopicListCell() {
            btnUnsubscribe.setOnAction(e ->
                MqttConnection.getInstance().unsubscribe(getItem())
            );
            this.setOnMouseEntered(e ->
                btnUnsubscribe.setVisible(true)
            );
            this.setOnMouseExited(e ->
                btnUnsubscribe.setVisible(false)
            );
            listCellPane.add(lSubscribedTopic, 0, 0);
            listCellPane.add(btnUnsubscribe, 1, 0);
            ColumnConstraints col0 = new ColumnConstraints();
            col0.setHalignment(HPos.LEFT);
            ColumnConstraints col1 = new ColumnConstraints();
            col1.setHalignment(HPos.RIGHT);
            col1.setHgrow(Priority.ALWAYS);
            listCellPane.getColumnConstraints().addAll(col0, col1);
            btnUnsubscribe.setVisible(false);
            btnUnsubscribe.setGraphic(ivBtnGraphic);
            btnUnsubscribe.setBackground(Background.EMPTY);
    }

    @Override
    protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        setGraphic(null);
        if (!empty && item != null) {
            lSubscribedTopic.setText(item);
            setGraphic(listCellPane);
        }
    }
}

10-07 12:40
查看更多