带有自定义 CellFactory 的 ListView 修剪不可见节点

Posted

技术标签:

【中文标题】带有自定义 CellFactory 的 ListView 修剪不可见节点【英文标题】:ListView with custom CellFactory trims invisible nodes 【发布时间】:2017-08-18 09:59:23 【问题描述】:

我的布局问题

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

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

CellFactory 所涉及的各种类扩展了Group,我尝试了其他一些Parent,到目前为止没有太大的成功。


如何重现

我没有分享我的StarShapeStarRow 和其他一些杂项课程(如果有要求,我很乐意)我写了一个重现该问题的示例。该类扩展 Application 并覆盖 start(...) 方法,如下所示:

@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);
            
        
    ;

上面将在true 项目前面显示一个带有黑色形状的ListView&lt;Boolean&gt;(因为tabShape.setVisible(item); 位)。 false 项目看起来像普通的 Label 对象,好像它们的 Group 中的不可见形状不存在(但确实存在)。


关闭 cmets

对此进行调试,结果发现具有不可见形状的组被赋予负的 layoutX 属性值。因此Label 控件没有像我希望的那样对齐。当我在 ListView 之外调用 setLayoutXsetLayoutY 时不会发生这种情况(不可见的形状会强制偏移),但它可能不是唯一会发生这种情况的地方。

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

【问题讨论】:

标签在其容器内居中的事实是问题#1,可能与矩形形状问题无关。 label.setAlignment(Pos.CENTER_LEFT); #2 之类的东西:你似乎需要隐形形状作为没有星级的物品的占位符?尝试使矩形透明而不是不可见。或者采用呈现评分栏的代码,并添加一个零星版本。 ...现在没有人敢因为这个disclaimer on meta而将其详细说明为答案 @dlatikay 好吧...用户仍然有权忽略免责声明并回答您知道的...并且任何强大到做到这一点并从社区获得 2 票的人将获得一半的赏金; ) @dlatikay:是的,需要占位符,因此所有“星标标签”都具有相同的宽度和高度。您的建议很好,目前无法尝试,但很快就会尝试。 @dlatikay 免责声明是voided by OP。赏金仍然可用,因为它不能授予我自己。 【参考方案1】:

取自@dlatikay's comment,您可以将它们的不透明度设置为0.0,而不是将占位符项目设置为不可见,而使其透明。

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

tabShape.setVisible(item);

与:

tabShape.setOpacity(item ? 1.0 : 0.0);

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

我看到的好处是:

    它不仅表示列表中某个项目的评分,还表示最高评分。 它避免了零星列表项的尴尬空白。

【讨论】:

您可能希望通过样式类来做到这一点,例如tabShape.getStyleClass().add("empty-star") 然后调整样式表中的不透明度,例如.empty-star -fx-opacity: 0.1; 【参考方案2】:

我猜我走错了

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

您只需更改 1 行即可实现您的目标。即,将Group 更改为HBox

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

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


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

【讨论】:

如果可以的话,我也会接受。但是,虽然它确实解决了我的 MCVE 的问题,但我的星标标签支持相对于标签的各种星形定位以及垂直对齐。因此,我宁愿扩展 Group 并避免变量间接指向 HBox 或 VBox。我还是从中学到了,谢谢! @Lulero 没问题。很高兴有帮助。我倾向于尽可能避免使用GroupGroup 有点类似于 CSS 中的绝对定位 - 有时它是完成工作的唯一方法,其余时间你应该避免它。

以上是关于带有自定义 CellFactory 的 ListView 修剪不可见节点的主要内容,如果未能解决你的问题,请参考以下文章

为啥当设备在 android 中水平时,ListView 内的嵌套自定义视图会阻止正确聚焦?

将整数映射到组合框中的自定义类的字符串

如何用文本图片和复选框过滤自定义listView?

scrollview里面嵌套listview的坑

带有自定义控件和自定义 StringProperty 的 Proguard

带有自定义按钮的自定义 UIAlertView