ListView with custom CellFactory clipping invisible nodes

My question is with the layout

I have a little problem with ListView , and I'm not sure if it is due to some knowledge that I lost, or if my approach is wrong. I must admit, I have not yet understood how JavaFX handles the layout in many possible cases.

ListView trims my layout attempt

The above screenshot shows the result that I get twice with the same code, except in the second that the invisible form that I use for the coherent layout becomes visible for debugging .

The various classes involved in CellFactory are expanding by the Group , I have tried with some other Parent without much success so far.




How to play

Instead of sharing my StarShape , StarRow and some other misc classes (I would be glad if it was necessary), I wrote a sample reproducing the problem. The class extends Application and overrides the start(...) method as such:

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

where this::cellFactory is

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

In the above example, a ListView<Boolean> will be displayed with black shapes in front of the true elements (due to the bits of tabShape.setVisible(item); ). false elements look like regular Label objects, as if there was no invisible form in their Group (but it is).




Close comments

Debugging this, it turns out that groups with invisible shapes get negative values ​​for the layoutX property . Therefore, the Label controls are not aligned as desired. This does not happen when I call setLayoutX and setLayoutY outside of the ListView (invisible shapes force offsets), but this is probably not the only place where this will happen.

What is happening and how to avoid it? Alternatively, how do I assume that I am approaching this wrong , which would be the right way? In other words, what is the question I should ask instead?

+45
java listview layout javafx
Mar 25 '17 at 13:20
source share
2 answers

By taking a comment from @dlatikay , instead of specifying the placeholder elements invisible, you can make them transparent by setting their opacity to 0.0 .

For MCVE, from your question this will be done by replacing:

 tabShape.setVisible(item); 

from:

 tabShape.setOpacity(item ? 1.0 : 0.0); 

As for the user experience, you can take it one step further. Instead of the "inactive" stars being completely transparent, you could set them almost transparent, as in this layout (with opacity set to 0.1 ):

mockup

The benefits that I see:

  • It indicates not only the rating of an item in the list, but also the maximum rating.
  • This avoids uncomfortable empty spaces for list items with zero stars.
+13
Apr 05 '17 at 5:32 on
source share

I guess I'm approaching this wrong

No, it is not. As with all layouts, there are often several ways to approach the same problem. Your approach is really right, and you are very close to a working solution.

You can achieve what you need with just one line. That is, changing Group to HBox .

An HBox ensures that items are ordered horizontally, one after the other. They also allow invisible elements to still occupy space.

I also commented on one line: label.setLayoutX(40) . I did this because HBox will not respect this setting, and in fact you do not need it. It will automatically move the elements horizontally as much as required.

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

When I make these changes, your layout will look like this:

enter image description here




It is important . Your example and your screenshots are slightly different. You can use VBox for your example on a star (V for 'vertical', H for 'horizontal').

+4
Apr 05 '17 at 12:51 on
source share



All Articles