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

我的布局问题

我有一个ListView问题,我不知道是否是因为我缺less一些知识,或者如果我的方法有缺陷。 不得不承认,我还不清楚JavaFX在很多可能的情况下如何处理布局。

ListView修剪我的布局尝试

上面的屏幕截图显示了我使用完全相同的代码得到两次的结果,只是在第二个屏幕上显示了用于debugging 的可见形状

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


如何重现

我没有分享我的StarShapeStarRow和其他一些misc类(如果需要我很乐意),我写了一个样例来重现这个问题。 这个类扩展了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); } } }; } 

以上将显示一个ListView<Boolean> ,在true项目前面有黑色的形状(因为tabShape.setVisible(item); bit)。 false项目看起来像普通的Label对象,就好像Group中不可见的形状不存在一样(但它是)。


闭幕评论

debugging这个,结果是具有不可见形状的组被赋予了负的layoutX属性值 。 因此, Label控件不会按照我希望的那样alignment。 当我在ListView之外调用setLayoutXsetLayoutY (不可见的形状确实会强制偏移)时,并不会发生这种情况,但它可能不是唯一会发生的地方。

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

从@ dlatikay的评论 ,而不是设置占位符项目不可见,您可以通过将其不透明度设置为0.0呈现透明。

从你的问题应用到MCVE,这将通过replace:

 tabShape.setVisible(item); 

有:

 tabShape.setOpacity(item ? 1.0 : 0.0); 

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

小样

我看到的好处是:

  1. 它不仅表示一个项目在列表中的评级,而且还表示最大评级。
  2. 它避免了零星的列表项的空白空间。

我猜我正在接近这个错误

不你不是。 和所有的布局一样,通常有多种方法来解决同样的问题。 你的方法实际上是正确的,你非常接近一个工作的解决scheme。

只需要换一行就可以达到目标。 也就是说,把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代表“水平”)。