4

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?

4

2 に答える 2

4

まさにあなたが説明している目的のために、私はButtonGridPane. これは初稿であり、まだ改善の余地がありますが、基本的なアイデアを得ることができるかもしれません。

public class ButtonGrid extends Pane {

    private static final double DEFAULT_RATIO = 0.618033987;

    private int                 columnCount;
    private double              ratio;

    public ButtonGrid(int columnCount) {
        this(columnCount, DEFAULT_RATIO);
    }

    public ButtonGrid(int columnCount, double heightToWidthRatio) {
        getStyleClass().add("button-grid");
        this.columnCount = columnCount;
        ratio = heightToWidthRatio;
    }

    public void setColumnCount(int columnCount) {
        this.columnCount = columnCount;
    }

    public void setHeightToWidthRatio(double ratio) {
        this.ratio = ratio;
    }

    @Override
    public Orientation getContentBias() {
        return Orientation.HORIZONTAL;
    }

    @Override
    protected void layoutChildren() {
        double left = getInsets().getLeft();
        double top = getInsets().getTop();

        double tileWidth = calculateTileWidth(getWidth());
        double tileHeight = calculateTileHeight(getWidth());

        ObservableList<Node> children = getChildren();
        double currentX = left;
        double currentY = top;
        for (int idx = 0; idx < children.size(); idx++) {
            if (idx > 0 && idx % columnCount == 0) {
                currentX = left;
                currentY = currentY + tileHeight;
            }
            children.get(idx).resize(tileWidth, tileHeight);
            children.get(idx).relocate(currentX, currentY);
            currentX = currentX + tileWidth;
        }
    }

    @Override
    protected double computePrefWidth(double height) {
        double w = 0;
        for (int idx = 0; idx < columnCount; idx++) {
            Node node = getChildren().get(idx);
            w += node.prefWidth(-1);
        }
        return getInsets().getLeft() + w + getInsets().getRight();
    }

    @Override
    protected double computePrefHeight(double width) {
        double h = calculateTileHeight(width) * getRowCount();
        return getInsets().getTop() + h + getInsets().getBottom();
    }

    private double calculateTileHeight(double width) {
        return calculateTileWidth(width) * ratio;
    }

    private double calculateTileWidth(double width) {
        return (-getInsets().getLeft() + width - getInsets().getRight()) / columnCount;
    }

    private int getRowCount() {
        return getChildren().size() / columnCount;
    }
}
于 2016-04-25T04:11:29.583 に答える