带有自定义 CellFactory 的 ListView 修剪不可见节点
Posted
技术标签:
【中文标题】带有自定义 CellFactory 的 ListView 修剪不可见节点【英文标题】:ListView with custom CellFactory trims invisible nodes 【发布时间】:2017-08-18 09:59:23 【问题描述】:我的布局问题
我对@987654325@ 有一点问题,我不确定是因为我缺少一些知识还是我的方法有缺陷。不得不承认,在许多可能的情况下,我还不清楚 JavaFX 如何处理布局。
上面的屏幕截图显示了我使用完全相同的代码两次得到的结果,除了 在第二个上一个不可见的形状我用于连贯布局对调试可见.
CellFactory
所涉及的各种类扩展了Group
,我尝试了其他一些Parent
,到目前为止没有太大的成功。
如何重现
我没有分享我的StarShape
、StarRow
和其他一些杂项课程(如果有要求,我很乐意)我写了一个重现该问题的示例。该类扩展 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<Boolean>
(因为tabShape.setVisible(item);
位)。 false
项目看起来像普通的 Label
对象,好像它们的 Group
中的不可见形状不存在(但确实存在)。
关闭 cmets
对此进行调试,结果发现具有不可见形状的组被赋予负的 layoutX 属性值。因此Label
控件没有像我希望的那样对齐。当我在 ListView
之外调用 setLayoutX
和 setLayoutY
时不会发生这种情况(不可见的形状会强制偏移),但它可能不是唯一会发生这种情况的地方。
发生了什么以及如何避免它?或者,我猜我正在接近这个错误,正确的方法是什么?换句话说,我应该问什么问题而不是这个?
【问题讨论】:
标签在其容器内居中的事实是问题#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 没问题。很高兴有帮助。我倾向于尽可能避免使用Group
。 Group
有点类似于 CSS 中的绝对定位 - 有时它是完成工作的唯一方法,其余时间你应该避免它。以上是关于带有自定义 CellFactory 的 ListView 修剪不可见节点的主要内容,如果未能解决你的问题,请参考以下文章
为啥当设备在 android 中水平时,ListView 内的嵌套自定义视图会阻止正确聚焦?