【问题标题】:JavaFX GridPane cells not auto-sizing to contentJavaFX GridPane 单元格不会根据内容自动调整大小
【发布时间】:2016-09-30 03:46:18
【问题描述】:

我正在编写一个生成随机颜色网格的 JavaFX 程序。调整窗口大小时,网格应调整为尽可能大,同时仍保持方形并在底部留出空间用于文本。

一切正常,但问题我遇到的是,调整大小时 GridPane 中留下的空隙很小。调整窗口大小时,间隙的大小会略有变化。有人可以帮我弄清楚如何消除这些差距吗?我包括完整的代码。它不会太长。谢谢。

import java.util.Random;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;

/**
 * Uses a 2D array to populate a grid of squares with random colors.
 */
public class Lab7 extends Application {

    private Color[][] colorGrid = new Color[8][8];
    private int redCellCount = 0;

    /**
     * Tells the program to start with the start() method since it is a JavaFX
     * Application
     * 
     * @param args
     *            arguments supplied to the program
     */
    public static void main(String[] args) {
        Application.launch(args);
    }

    /**
     * Constructor for the class instantiates the 2D array of Color objects with
     * random colors.
     */
    public Lab7() {
        // array of 12 awt colors not including white since it is used as the
        // background color
        Color[] colorList = { Color.BLACK, Color.BLUE, Color.CYAN, Color.DARKGREY, Color.GRAY, Color.GREEN,
                Color.LIGHTGRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.YELLOW };

        // populate the 2D array of colors with random colors from the colorList
        for (int i = 0; i < colorGrid.length; i++) {
            for (int j = 0; j < colorGrid[i].length; j++) {
                Random rand = new Random();
                int colorCode = rand.nextInt(12);
                if (colorCode == 10) {
                    redCellCount++;
                }
                colorGrid[i][j] = colorList[colorCode];
            }
        }
    }

    /*
     * overridden method of the Application class. This is the entry point of
     * the JavaFX application
     */
    @Override
    public void start(Stage stage) throws Exception {

        // a GridPane that will hold the checkerboard of colors
        GridPane checkerboardPane = new GridPane();

        // a root pane for the layout
        BorderPane parentPane = new BorderPane();

        // create the scene and set the root node as the BorderPane and have the
        // initial size be 400x400 pixels and set the background color to white
        Scene scene = new Scene(parentPane, 400, 400);
        parentPane.setStyle("-fx-background-color: " + toRGBCode(Color.WHITE));

        // a Text object to display the number of red squares
        Text redCellCountText = new Text("There are " + redCellCount + " red squares.");

        // put the colorGrid in the center of the GridPane and the
        // redCellCountText to the bottom
        parentPane.setCenter(checkerboardPane);
        parentPane.setBottom(redCellCountText);

        // create 64 rectangles, fill them with the colors in the colorGrid and
        // set a mouse click event handler
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {

                Rectangle rect = new Rectangle(scene.getWidth() / 8, scene.getWidth() / 8);

                // bind the width property of the rectangle to 1/8 of the
                // smaller of the scene width or height, leave 50 pixels at the
                // bottom for the Text
                rect.widthProperty()
                        .bind(Bindings
                                .when(scene.widthProperty().lessThanOrEqualTo(scene.heightProperty().subtract(50)))
                                .then(scene.widthProperty().divide(8))
                                .otherwise(scene.heightProperty().subtract(50).divide(8)));
                // bind the width of the rectangle to its height so it will
                // always be square
                rect.heightProperty().bind(rect.widthProperty());
                // set the color of the rectangle to correspond to the colorGrid
                rect.setStyle("-fx-fill: " + toRGBCode(colorGrid[i][j]));
                // set an event listener for the rectangle to handle when the
                // user clicks on it
                rect.setOnMouseClicked(new EventHandler<MouseEvent>() {

                    @Override
                    public void handle(MouseEvent event) {

                        // if the rectangle is not red
                        if (!((Rectangle) event.getSource()).getFill().equals(Color.RED)) {
                            // set its color to red
                            ((Rectangle) event.getSource()).setFill(Color.RED);
                            // increment the redCellCount and update the text
                            redCellCount++;
                            redCellCountText.setText("There are " + redCellCount + " red squares.");
                        }
                    }

                });
                // add the rectangle to its respective square in the GridPane
                checkerboardPane.add(rect, j, i);
            }
        }
        // set the scene in the stage and set its title
        stage.setScene(scene);
        stage.setTitle("Lab7");
        // show the stage to make it visible
        stage.show();
    }

    /**
     * 
     * @param color
     *            The JavaFX Color to convert
     * @return the rgb code representing the JavaFX Color
     */
    public static String toRGBCode(Color color) {
        return String.format("#%02X%02X%02X", (int) (color.getRed() * 255), (int) (color.getGreen() * 255),
                (int) (color.getBlue() * 255));
    }
}

【问题讨论】:

    标签: java user-interface javafx gridpane


    【解决方案1】:

    嗯,这些小差距来自于计算所有Rectangles 的宽度,当您将值绑定到Scene 的高度时。

    肮脏的解决方案是使用GridPane.setMargin(Node child, Insets value)在矩形周围添加-1边距:

    // add the rectangle to its respective square in the GridPane
    checkerboardPane.add(rect, j, i);
    GridPane.setMargin(rect, new Insets(-1, -1, -1, -1));
    

    【讨论】:

    • 感谢这工作非常好。为什么称其为“肮脏的解决方案”?
    • 因为@fabian 的建议更干净。为了简单起见,我只是提出了这个:)
    【解决方案2】:

    GridPane在布局过程中使用节点的宽度。因为除以 8 的结果可能不是整数。默认情况下,GridPane 会将其子项的边界坐标四舍五入为整数值,这会导致您观察到的差距。

    snapToPixel设置为false可以达到更好的效果:

    checkerboardPane.setSnapToPixel(false);
    

    虽然结果并不完美。如果您完全想消除间隙,请自行将大小四舍五入为整数值。此外,为了提高效率,只需为大小创建一个 NumberBinding。此外,Bindings.min 可以帮助您获得更简单的尺寸代码。 Shapefill 也可以直接设置为 Color

    NumberBinding size = Bindings.min(scene.widthProperty(),
                                      scene.heightProperty().subtract(50))
                                 .divide(8);
    
    // binding for size rounded down
    NumberBinding roundedSize = Bindings.createIntegerBinding(() -> size.intValue(), size);
    
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            Rectangle rect = new Rectangle();
    
            rect.widthProperty().bind(roundedSize);
            rect.heightProperty().bind(roundedSize);
            rect.setFill(colorGrid[i][j]);
    
            // click handler
    
            checkerboardPane.add(rect, j, i);
        }
    }
    

    【讨论】:

    • 谢谢。你是对的 setSnapToPixel(false) 使它更好一点,但仍然不完美。至于四舍五入,我也想到了这个并尝试了这个:
    • rect.widthProperty() .bind(Bindings .when(scene.widthProperty().lessThanOrEqualTo(scene.heightProperty().subtract(50))) .then(scene.widthProperty().subtract(scene.widthProperty().intValue() % 8).divide(8)) .otherwise(scene.heightProperty().subtract(50).subtract(scene.widthProperty().intValue() % 8).divide(8))); 但它仍然不起作用。我的逻辑在这里吗?谢谢
    • 我的解决方案不适合您吗?通过使用模运算符,您只需使绑定 more 复杂,而不是 less 复杂。由于您的最终结果是您比较的值中的较小值,因此应该应用 min 而不是使用大小写区分手动执行它,IntegerBinding 返回向下舍入到 int 的值...
    猜你喜欢
    • 2016-10-04
    • 2021-09-28
    • 2017-02-28
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 2019-01-31
    • 2016-01-12
    • 2014-06-10
    相关资源
    最近更新 更多