Estoy creando una aplicación GUI de Java simple usando JavaFX que tiene un panel de borde como nodo raíz.
En la sección superior del Panel de borde, hay un Panel de cuadrícula con tres columnas (top Grid Pane de ahora en adelante).
En la primera columna del panel de cuadrícula superior, hay un botón de inicio, en la segunda columna, hay una región vacía que solo sirve como spacer entre la primera y la tercera columna del panel de cuadrícula superior, y en el tercera columna, hay otro GridPane (right Grid Pane de ahora en adelante).
El panel de cuadrícula derecho contiene un botón (botón de inicio de sesión) al inicio. Sin embargo, cuando un usuario inicia sesión correctamente en la aplicación, se agregan otros dos botones y una etiqueta al panel de cuadrícula derecho como parte del evento de clic del botón Iniciar sesión.
El espaciador maxWidthProperty y minWidthProperty están vinculados al panel de cuadrícula superior (tgp) widthProperty y al panel de cuadrícula derecho (rgp) widthProperty de esta manera:

spacer.minWidthProperty().bind(tgp.widthProperty().subtract(80).subtract(rgp.widthProperty()).subtract(3));
spacer.maxWidthProperty().bind(tgp.widthProperty().subtract(80).subtract(rgp.widthProperty()).subtract(3));

Lo que hace que el panel de cuadrícula derecho se mueva bien con sus botones que permanecen en el lado derecho de la escena cuando un usuario cambia el tamaño del escenario principal.
Sin embargo, se produce un problema cuando el usuario inicia sesión y se agregan botones adicionales al Panel de cuadrícula derecho. El espaciador de alguna manera pasa por alto este cambio y su ancho permanece igual, lo que hace que los botones adicionales aparezcan fuera del ancho del escenario actual. La única forma de actualizar el ancho del espaciador es interactuar con el escenario de alguna manera, haciendo clic en minimizar / maximizar / restaurar o haciendo clic en cualquier botón de la escena.
¿Hay alguna manera de actualizar automáticamente el ancho de la región después de que se modifiquen los nodos a los que está vinculado su ancho? ¿O hay un mejor enfoque para hacer un Panel de cuadrícula superior con un botón a la izquierda y un número modificable de botones (nodos) a la derecha?

Editar: aquí hay una demostración del problema con varias capturas de pantalla apiladas una sobre otra: ingrese la descripción de la imagen aquí

Ejemplo reproducible mínimo:

BorderPane root = new BorderPane();

GridPane tgp = new GridPane();
tgp.minWidthProperty().bind(root.widthProperty());
tgp.maxWidthProperty().bind(root.widthProperty());
tgp.setStyle("-fx-background-color: WHITE; -fx-border-color: LIGHTGREY;");
tgp.setMinHeight(37);
tgp.setMaxHeight(37);
root.setTop(tgp);

Button homeButton = new Button("Home"));
homeButton.setMinHeight(35);
homeButton.setMaxHeight(35);
homeButton.setMinWidth(80);
homeButton.setMaxWidth(80);
tgp.add(homeButton, 0, 0);

GridPane rgp = new GridPane(); // Right Grid Pane - holds User related nodes
rgp.setHgap(5);
tgp.add(rgp, 2, 0);

Label unl = new Label("My Profile");
unl.setFont(new Font("Calibri", 15));
unl.setTextFill(Color.RED);
unl.setMinWidth(Region.USE_PREF_SIZE);

Button wlButton = new Button("Watchlist");
wlButton.setMinHeight(35);
wlButton.setMaxHeight(35);
wlButton.setMinWidth(80);
wlButton.setMaxWidth(80);

Button cartButton = new Button("Cart");
cartButton.setMinHeight(35);
cartButton.setMaxHeight(35);
cartButton.setMinWidth(60);
cartButton.setMaxWidth(60);

Button logInOutButton = new Button("Log In");
logInOutButton.setMinHeight(35);
logInOutButton.setMaxHeight(35);
logInOutButton.setMinWidth(60);
logInOutButton.setMaxWidth(60);
rgp.add(logInOutButton, 3, 0);

logInOutButton.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    if (logInOutButton.getText().equals("Log In")) {
                        LogInStage lis = new LogInStage();
                        lis.initStage();

                        if (lis.username != null) {
                            logInOutButton.setText("Log Out");
                            rgp.add(unl, 0, 0);
                            rgp.add(wlButton, 1, 0);
                            rgp.add(cartButton, 2, 0);
                        }

                    } else if (logInOutButton.getText().equals("Log Out")) {
                        logInOutButton.setText("Log In");
                        rgp.getChildren().remove(unl);
                        rgp.getChildren().remove(wlButton);
                        rgp.getChildren().remove(cartButton);
                    }

                }
            });

Region spacer = new Region();
spacer.minWidthProperty().bind(tgp.widthProperty().subtract(80).subtract(rgp.widthProperty()).subtract(3));
spacer.maxWidthProperty().bind(tgp.widthProperty().subtract(80).subtract(rgp.widthProperty()).subtract(3));
tgp.add(spacer, 1, 0)
-1
A6EE 28 sep. 2019 a las 19:35

1 respuesta

La mejor respuesta

Siempre es una mala idea usar enlaces, si puede evitarlo. Cualquier cambio en las restricciones de tamaño puede llevar a que se programe una nueva pasada de diseño, pero durante la pasada de diseño se supone que son constantes. Si ahora introduce un enlace, podría suceder la siguiente secuencia de eventos:

  1. Se solicita un pase de diseño para el GridPane, se debe establecer una bandera para indicar que se requiere un diseño
  2. Ocurre un pase de diseño. Durante el pase de diseño, se cambia el tamaño de los niños. Esto desencadena una actualización de las restricciones de los hijos con las vinculaciones.
  3. La bandera se borra, pero los cambios en la restricción ya ocurrieron. El diseño no reflejará esto. El GridPane tiene otra razón para hacer un diseño.

No sé cómo está configurada su escena en detalle, pero recomiendo usar restricciones de columna: establezca las prioridades de crecimiento para las externas en SOMETIMES y la del centro en ALWAYS. Si necesita algo de espacio alrededor de los niños, puede usar GridPane.setMargin (o el relleno del GridPane en sí mismo, si necesita una distancia a los bordes para todos los niños).

@Override
public void start(Stage primaryStage) {
    Button[] rightContent = new Button[3];
    for (int i = 0; i < rightContent.length; i++) {
        Button btn = new Button(Integer.toString(i));
        GridPane.setColumnIndex(btn, i);
        rightContent[i] = btn;
    }

    Button cycle = new Button("cycle");
    GridPane rgp = new GridPane(); // I would usually use a HBox here
    // don't grow larger than needed
    rgp.setMaxWidth(Region.USE_PREF_SIZE);

    // cycle though 0 to 3 buttons on the right
    cycle.setOnAction(new EventHandler<ActionEvent>() {
        int nextIndex = 0;

        @Override
        public void handle(ActionEvent event) {
            if (nextIndex >= rightContent.length) {
                rgp.getChildren().clear();
                nextIndex = 0;
            } else {
                rgp.getChildren().add(rightContent[nextIndex]);
                nextIndex++;
            }

        }
    });

    ColumnConstraints sideConstraints = new ColumnConstraints();
    sideConstraints.setHgrow(Priority.SOMETIMES);

    ColumnConstraints centerConstraints = new ColumnConstraints();
    centerConstraints.setHgrow(Priority.ALWAYS);

    //prefer to grow the center part of the GridPane
    GridPane root = new GridPane();
    root.getColumnConstraints().addAll(sideConstraints, centerConstraints, sideConstraints);

    root.add(cycle, 0, 0);
    root.add(rgp, 2, 0);

    // add something to visualize the center part
    // you could simply leave this part out
    Region center = new Region();
    center.setStyle("-fx-border-radius: 10;-fx-border-width: 1;-fx-border-color:black;");
    root.add(center, 1, 0);

    Scene scene = new Scene(root, 400, 300);
    primaryStage.setScene(scene);
    primaryStage.show();
}

Como se menciona en los comentarios, la región center no es realmente necesaria.

0
fabian 28 sep. 2019 a las 18:42