具有自定义单元格工厂的 ListView 修剪不可见节点

2022-09-01 00:41:19

我的布局问题

我有一点问题,我不确定是因为我缺少一些知识还是我的方法有缺陷。不得不承认,我还不清楚JavaFX在很多可能的情况下如何处理布局。ListView

ListView trims my attempt at layout

上面的屏幕截图显示了我使用完全相同的代码获得两次的结果,除了在第二个屏幕截图上,我用于连贯布局的不可见形状对于调试是可见的

扩展所涉及的各种类,我尝试了其他一些,但到目前为止没有取得多大成功。CellFactoryGroupParent


如何重现

我没有分享我的,和其他一些杂项类(如果需要,我很乐意这样做),而是写了一个示例来复制这个问题。该类按如下方式扩展和重写该方法:StarShapeStarRowApplicationstart(...)

@Override
public void start(Stage primaryStage) throws Exception {
    final StackPane root = new StackPane();
    final Scene scene = new Scene(root, 400, 600);

    final ListView<Boolean> listView = new ListView<>();
    listView.setCellFactory(this::cellFactory);

    for (int i = 0; i < 5 ; i++) {
        listView.getItems().add(true);
        listView.getItems().add(false);
    }

    root.getChildren().add(listView);

    primaryStage.setScene(scene);
    primaryStage.setTitle("ListView trims the invisible");
    primaryStage.show();
}

在哪里this::cellFactory

private ListCell<Boolean> cellFactory(ListView<Boolean> listView) {
    return new ListCell<Boolean>() {
        @Override
        protected void updateItem(Boolean item, boolean empty) {
            super.updateItem(item, empty);

            if (empty || item == null) {
                setText(null);
            } else {
                final Rectangle tabShape = new Rectangle();
                tabShape.setHeight(20);
                tabShape.setWidth(40);
                tabShape.setVisible(item);

                final Label label = new Label(item.toString());
                label.setLayoutX(40);

                final Group cellRoot = new Group();
                cellRoot.getChildren().add(tabShape);
                cellRoot.getChildren().add(label);

                setGraphic(cellRoot);
            }
        }
    };
}

上面将在项目前面显示带有黑色形状的(因为位)。这些物品看起来像常规对象,就好像它们中的隐形形状不存在一样(但它是)。ListView<Boolean>truetabShape.setVisible(item);falseLabelGroup


结束语

对此进行调试后,事实证明,具有不可见形状的组被赋予负布局X属性值。因此,控件并不像我希望的那样对齐。当我调用和在 a 之外时,它不会发生(不可见的形状会强制偏移),但它可能不是唯一会发生的地方。LabelsetLayoutXsetLayoutYListView

发生了什么以及如何避免?或者,正如我猜测我处理错了,什么是正确的方法?换句话说,我应该问什么问题而不是这个?


答案 1

自@dlatikay的注释,您可以通过将其不透明度设置为 来使它们透明,而不是将占位符项设置为不可见。0.0

从您的问题中应用于MCVE,这将通过替换以下内容来完成:

tabShape.setVisible(item);

跟:

tabShape.setOpacity(item ? 1.0 : 0.0);

在用户体验方面,您可以更进一步。您可以将“非活动”星号设置为完全透明,而不是将它们设置为接近透明,就像在此模型中一样(不透明度设置为):0.1

mockup

我看到的好处是:

  1. 它不仅指示列表中项目的评级,还指示最大评级。
  2. 它避免了零星号列表项的尴尬空白区域。

答案 2

我猜我走错了路

不,你不是。与所有布局一样,通常有多种方法可以解决同一问题。您的方法实际上是正确的,并且您非常接近一个有效的解决方案。

您只需更改1行即可实现所追求的目标。也就是说,将更改为 HBox

An 确保元素一个接一个地水平排序。它们还允许看不见的元素仍然占用空间。HBox

我还注释了一行: .我这样做是因为不会尊重这个设置,实际上你不需要它。它会自动将元素水平移动所需的数量。label.setLayoutX(40)HBox

@Override
protected void updateItem(Boolean item, boolean empty) {
    super.updateItem(item, empty);

    if (empty || item == null) {
        setText(null);
    }
    else {
        final Rectangle tabShape = new Rectangle();
        tabShape.setHeight(20);
        tabShape.setWidth(40);
        tabShape.setVisible(item);

        final Label label = new Label(item.toString());
        //label.setLayoutX(40);

        final HBox cellRoot = new HBox();
        cellRoot.getChildren().add(tabShape);
        cellRoot.getChildren().add(label);

        setGraphic(cellRoot);
    }
}

当我进行这些更改时,您的布局将按如下方式呈现:

enter image description here


重要提示:您的示例和屏幕截图略有不同。您可能希望使用 VBox 作为星形示例(V 表示“垂直”,H 表示“水平”)。