Должно ли указание CSS-границ JavaFX для содержимого GridPane сворачивать размер?

Я пытался расположить сцену JavaFX, используя GridPane когда я столкнулся со следующей проблемой. Если я настрою сетку с соответствующими ограничениями и добавлю новые экземпляры StackPane Кроме того, размер сцены, сцены и ее содержимого по умолчанию обеспечивает видимость содержимого:

Без CSS

Тем не менее, если я добавлю стиль JavaFX CSS, указав границу для нового экземпляра StackPane прежде чем добавить его в GridPane тогда размер по умолчанию для вещей, кажется, завершается:

С CSS

Мой код выглядит следующим образом:

public static void main(final String[] args) {
    Platform.startup(() -> {});
    Platform.runLater(() -> {

        final GridPane gridPane = new GridPane();
        final Scene scene = new Scene(gridPane);
        final Stage stage = new Stage();
        stage.setScene(scene);

        final List<StackPane> panes = new ArrayList<>();
        for (int i = 0; i < 4; i++) {

            // Create a new pane with a random background color for
            // illustration
            final StackPane p = createNewPane();
            panes.add(p);

            // The addition / removal of the following line affects the
            // layout.
            p.setStyle("-fx-border-width:2px;-fx-border-color:red");
        }

        for (int r = 0; r < 2; r++) {
            final RowConstraints rc = new RowConstraints();
            rc.setPercentHeight(50);
            gridPane.getRowConstraints().add(rc);
        }
        for (int c = 0; c < 2; c++) {
            final ColumnConstraints cc = new ColumnConstraints();
            cc.setPercentWidth(50);
            gridPane.getColumnConstraints().add(cc);
        }

        for (int r = 0, i = 0; r < 2; r++) {
            for (int c = 0; c < 2; c++) {
                gridPane.add(panes.get(i++), c, r);
            }
        }
        stage.show();
    });
}

Любопытно, если я перееду stage.show() сразу после того, как я установил сцену, то все отлично работает даже с CSS.

Может ли кто-нибудь помочь мне понять, один, это ожидаемое поведение, и два, почему порядок выполнения stage.show() имеет значение?

Спасибо!

1 ответ

Решение

В чем проблема

Ваш пример немного неоднозначен. Вы не устанавливаете предпочтительный размер чего-либо, добавленного в Сцену, в любое время. Таким образом, платформа JavaFX действительно может делать все, что угодно, с точки зрения определения размеров. Установка предпочтительного размера в процентах отличается от установки предпочтительного абсолютного размера. Размер в процентах является относительным, поэтому возникает вопрос, относительно чего? и ответ на этот вопрос неясен.

Что касается того, почему это происходит:

// The addition / removal of the following line affects the
// layout.
p.setStyle("-fx-border-width:2px;-fx-border-color:red");

Я не мог сказать. Я предполагаю, что использование CSS вызывает некоторую дополнительную логику компоновки, которая влияет на изменение размера при отсутствии каких-либо подсказок по размеру.

Как это исправить

В любом случае, решение состоит в том, чтобы просто сделать вещи более понятными и указать предпочтительный размер, по крайней мере, для чего-то в приложении, тогда приложение будет первоначально измерено до этого предпочтительного размера.

Вот пример:

import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Starter {
    public static void main(String[] args) {
        Platform.startup(() -> {});
        Platform.runLater(() -> {
            final GridPane gridPane = new GridPane();
            final Scene scene = new Scene(gridPane);
            final Stage stage = new Stage();
            stage.setScene(scene);

            final List<StackPane> panes = new ArrayList<>();
            for (int i = 0; i < 4; i++) {

                // Create a new pane with a random background color for
                // illustration
                final StackPane p = createNewPane();
                panes.add(p);

                // The addition / removal of the following line affects the
                // layout.
                p.setStyle("-fx-border-width:2px;-fx-border-color:red");
            }

            for (int r = 0; r < 2; r++) {
                final RowConstraints rc = new RowConstraints();
                rc.setPercentHeight(50);
                gridPane.getRowConstraints().add(rc);
            }
            for (int c = 0; c < 2; c++) {
                final ColumnConstraints cc = new ColumnConstraints();
                cc.setPercentWidth(50);
                gridPane.getColumnConstraints().add(cc);
            }

            for (int r = 0, i = 0; r < 2; r++) {
                for (int c = 0; c < 2; c++) {
                    gridPane.add(panes.get(i++), c, r);
                }
            }
            stage.show();
        });
    }

    private static final Random random = new Random(42);

    private static StackPane createNewPane() {
        StackPane pane = new StackPane();

        pane.setBackground(
                new Background(
                        new BackgroundFill(
                                randomColor(), null, null
                        )
                )
        );

        pane.setPrefSize(150, 100);

        return pane;
    }

    private static Color randomColor() {
        return Color.rgb(
                random.nextInt(256),
                random.nextInt(256),
                random.nextInt(256)
        );
    }
}

Ключевой частью решения является вызов:

pane.setPrefSize(150, 100);

который устанавливает предпочтительный размер для стековых панелей, которые были размещены в вашем макете.

В качестве альтернативы, вместо выполнения предпочтительного размера снизу вверх путем установки предпочтительного размера для каждой из стековых панелей, вы также можете выполнить аналогичную задачу с точки зрения сверху вниз, установив вместо этого соответствующие ограничения на GridPane, например:

 gridPane.setPrefSize(300, 200);

Заметка

Я бы посоветовал использовать класс приложения JavaFX вместо вызова Platform.startup(), если только нет веской причины использовать последний (что в данном случае есть - взаимодействие с Swing, как вы отметили в своем комментарии).

Другие вопросы по тегам