Is there any way in JavaFX to take best from both TilePane or FlowPane and GridPane?
Here's what I'd like to achieve:
First, I like the idea of GridPane where I can set up a M×N grid which automatically resizes within its parent container to divide the space equally into M columns and N rows. Then I can put some child elements which fill each cell completely, and they will stretch along with the grid. This is cool.
But there's one drawback: I need to explicitly specify where should each control go, by setting its row & column number.
Then, there are layout containers such as FlowPane or TilePane which automatically reflow their child elements when the parent container changes its size. When I add another child element, it is automatically attached at the end of the list, and the list of elements automatically wraps after reaching the edge of the container when there's too few space to fit another element in there.
But here's a drawback as well: the child elements can only have rigid, pre-defined sizes. They won't stretch with its parent element.
And here's what I need:
I need the best from both of these containers, that is, I want a M by N grid (let's say 4×4) where each cell is ParentWidth/M by ParentHeight/N and stretches along with the window, so that it is always 4×4 cells, but their sizes (along with the sizes of their contents) stretches accordingly.
But I don't want to tell the container explicitly in which row & column to put every new child I add there. Instead, I want to just add it and let the container figure out the first empty cell to put it in, filling the cells from left to right, then top to bottom if there's no empty cell left in the current row.
Is there some magical setup for any of these predefined containers' attributes which would allow me to achieve this? Or do I need to write such a container myself?
OK here's my own attempt at the solution:
Since the
GridPane
works almost the way I need, just doesn't automatically flow its contents, I decided to subclassGridPane
and add custom code for automatic flowing its child controls. (Thanks to @jns for the hint that JavaFX library control classes can be subclassed.)So I subclassed the
GridPane
and added a listener for its internal list of children, so that whenever a child is added or removed from that list, or the list changes in some way (e.g. we reorder the children), a private method is called to reflow the children in the grid by assigning them to correct columns and rows by their position in the list. I also added two private helper functions to convert the position in the list into row & column numbers and back.I know that this isn't perfect nor elegant, but hey, it works, and it works well enough for my needs :P so I publish the code here in case someone else had the same problem:
As you can see, I also used properties for the number of rows and columns, with getters and setters. When one uses a setter to change the number of rows or columns, the internal lists of column/row constraints inside
GridPane
are being recreated so that they resized properly, and the content is being reflown when the number of columns changes.There are also
@NamedArg
annotations in the constructor, so that one could create this control with an initial number of rows & columns from the layout described in an FXML file.Since it pretty much "solved" my problem in a way, I'm marking my answer as accepted. But if anyone will post a better solution, I'll gladly accept his answer then.
Also feel free to tell me if you have any suggestions about improvements of this code, or if you think something could be done more elegantly, since – as I said – it is hardly perfect yet.