Skip to content

3 Layouts

Introducción

Los layouts son contenedores usados para gestionar la disposición de los controles de grafo. Cuando una ventana es redimensionada, el panel automáticamente también es recolocado y redimensiona los componentes que contiene.

La principal ventaja de usar layouts es que el tamaño y el alineamiento de los nodos está gestionado por el panel, por lo que si el panel se redimensiona, los nodos hijos también lo harán.

En JavaFX todos los controles tienen un tamaño preferido (preferredSize) que se calcula en función de su contenido. Además, los controles también proporcionan un tamaño mínimo y máximo que se basan en el uso típico del control. Por ejemplo, un botón tiene como tamaño máximo su tamaño preferido. Este comportamiento puede ser manipulado con los métodos vistos en el apartado anterior.

Cuando se posiciona un control dentro de un layout, el control pasará a ser hijo del layout que se encargará de visualizarlo adecuadamente.

JavaFX tiene, entre otros, los siguientes paneles de layout:

  • StackPane: Coloca los nodos en una pila de atrás hacia adelante. El último nodo es el que está en la parte superior.
  • HBox y VBox. Los elementos son posicionados de forma horizontal o vertical, respectivamente.
  • FlowPane. Posiciona los elementos en una fila o columna. La orientación por defecto es horizontal.
  • AnchorPane. Permite anclar los bordes de los hijos a una distancia predefinida del borde del contenedor.
  • TilePane. Distribuye los elementos de tal forma que tengan el mismo tamaño. Por defecto, son colocados horizontalmente, pero puede configurarse de forma vertical o en forma de matriz.
  • BorderPane. Coloca los elementos en las posiciones, superior, inferior, derecha, izquierda o centro. Los elementos posicionados en la parte superior o inferior ocupan todo el ancho, pero el alto necesario. Mientras que, los posicionados en la parte izquierda o derecha, ocupan el ancho necesario y todo el alto. Los elementos posicionados en el centro ocupará todo el resto de espacio disponible.
  • GridPane. Posiciona los elementos en una matriz de filas y columnas. Los nodos pueden extenderse por múltiples filas y columnas.

StackPane

StackPane es una de las clases de contenedor más básicas en JavaFX y es parte del paquete javafx.scene.layout. Se utiliza para colocar nodos unos sobre otros en una pila (stack), donde cada nodo se coloca en el centro de su padre. Es útil cuando quieres superponer varios nodos o cuando deseas que un solo nodo ocupe todo el espacio disponible en su contenedor.

StackPane

Figura 6 - StackPane

Todos los nodos hijos de un StackPane se colocan uno encima del otro. El primer nodo añadido se coloca en el fondo y el último en la parte superior. De forma predeterminada, los nodos se centran en el medio del StackPane. StackPane puede ajustar el tamaño de sus nodos hijos para que se ajusten al tamaño del panel.

Con el método getChildren() obtenemos una lista con los nodos hijos del panel, y haciendo uso del método addAll() o add() de las listas, podemos añadir elementos al panel:

StackPane stack = new StackPane()

stack.getChildren().addAll(new Button('Button'))

A través del método estático setAlignment(Node child, Pos value) puedes cambiar la alineación de los nodos hijos. Con el método setAlignment(Pos value) cambiamos la alineación de todos sus hijos.

StackPane.setAlignment(button, POS.TOP_LEFT)

También podemos añadirle un margen al hijo, haciendo uso del método setMargin(Node children, Insets value)

Veamos un ejemplo:

public class StackPaneExample extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        StackPane stack = new StackPane();

        Button button1 = new Button("Button 1");
        button1.setPrefSize(150, 100);

        Button button2 = new Button("Button 2");
        button2.setPrefSize(100, 50);

        stack.getChildren().addAll(button1, button2);
        stack.setAlignment(Pos.BOTTOM_RIGHT);

        Scene scene = new Scene(stack, 300, 200);

        stage.setTitle("StackPane example");
        stage.setScene(scene);
        stage.show();

    }

    public static void main(String[] args) {
        launch();
    }
}

Como se puede observar los botones están uno encima del otro, ambos alineados a bajo a la derecha.

HBox y VBox

HBox y VBox son dos de los contenedores de diseño más utilizados en JavaFX para organizar nodos en una disposición horizontal o vertical, respectivamente. Ambos son parte del paquete javafx.scene.layout y facilitan la creación de interfaces de usuario al proporcionar una manera sencilla de alinear y distribuir nodos.

HBox organiza sus nodos hijos en una fila horizontal. Los nodos hijos se colocan uno al lado del otro en una fila. En su constructor, HBox(int gap) se puede indicar el espacio entre los elementos.

HBox

Figura 7 - HBox

VBox organiza sus nodos hijos en una columna vertical. Los nodos hijos se colocan uno debajo de otro en una columna. También, a través de su constructor, VBox(int gap) se le puede indicar el espacio entre los elementos.

VBox

Figura 8 - VBox

A través de el método setAlignment(Post ps) y el método estático setMargin(Node child, Insets inset), se puede indicar el alineamiento y el margen de los hijos, respectivamente. La alineación por defecto es a la izquierda. Veamos un ejemplo:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        Button button1 = new Button("Button 1");
        Button button2 = new Button("Button 2");
        Button button3 = new Button("Button 3");
        Button button4 = new Button("Button 4");
        Button button5 = new Button("Button 5");

        HBox hbox = new HBox(10);
        hbox.getChildren().addAll(button1, button2, button3, button4, button5);
        hbox.setAlignment(Pos.CENTER);

        Scene scene = new Scene(hbox, 300, 200);
        stage.setTitle("HBox Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}
public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) {
        Button button1 = new Button("Button 1");
        Button button2 = new Button("Button 2");
        Button button3 = new Button("Button 3");
        Button button4 = new Button("Button 4");
        Button button5 = new Button("Button 5");

        VBox vbox = new VBox(10);
        vbox.getChildren().addAll(button1, button2, button3, button4, button5);
        vbox.setAlignment(Pos.CENTER);

        Scene scene = new Scene(vbox, 300, 200);
        stage.setTitle("VBox Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

FlowPane

FlowPane es otro contenedor de diseño en JavaFX que organiza sus nodos hijos en una disposición de flujo. Los nodos se colocan en filas horizontales (o columnas verticales) y se ajustan automáticamente a medida que se agregan más nodos, envolviéndose a la siguiente fila (o columna) cuando se alcanza el límite del contenedor. Este comportamiento es similar al de un flujo de texto en un párrafo.

FlowPane

Figura 9 - FlowPane

A través del método setOrientation(Orientation o) se puede especificar la orientación del panel, que por defecto es horizontal. Con el constructor FlowPane(int hGap, int vGap) se pueden establecer el espaciado horizontal y vertical, respectivamente. Veamos un ejemplo:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage)  {
        Button button1 = new Button("Button 1");
        Button button2 = new Button("Button 2");
        Button button3 = new Button("Button 3");
        Button button4 = new Button("Button 4");
        Button button5 = new Button("Button 5");

        FlowPane flowPane = new FlowPane(10, 10);
        flowPane.setOrientation(Orientation.VERTICAL);
        flowPane.setAlignment(Pos.CENTER);
        flowPane.getChildren().addAll(button1, button2, button3, button4, button5);

        Scene scene = new Scene(flowPane, 300, 200);

        stage.setTitle("FlowPane Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

AnchorPane

AnchorPane es un contenedor de diseño en JavaFX que permite anclar nodos a los bordes del contenedor. Este tipo de pane es útil cuando deseas que ciertos elementos se mantengan en una posición fija en relación con los bordes del contenedor, incluso cuando el tamaño del contenedor cambia.

AnchorPane

Figura 10 - AnchorPane

Entre sus métodos comunes encontramos:

  • setTopAnchor(Node child, Double value): Establece la distancia desde el nodo hijo hasta el borde superior del AnchorPane.
  • setRightAnchor(Node child, Double value): Establece la distancia desde el nodo hijo hasta el borde derecho del AnchorPane.
  • setBottomAnchor(Node child, Double value): Establece la distancia desde el nodo hijo hasta el borde inferior del AnchorPane.
  • setLeftAnchor(Node child, Double value): Establece la distancia desde el nodo hijo hasta el borde izquierdo del AnchorPane.

Veamos un ejemplo básico:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage)  {
        Button button1 = new Button("Top Left");
        Button button2 = new Button("Bottom Right");
        Button button3 = new Button("Center");

        AnchorPane anchorPane = new AnchorPane();

        anchorPane.getChildren().addAll(button1, button2, button3);

        AnchorPane.setTopAnchor(button1, 10.0);
        AnchorPane.setLeftAnchor(button1, 10.0);

        AnchorPane.setBottomAnchor(button2, 10.0);
        AnchorPane.setRightAnchor(button2, 10.0);

        AnchorPane.setTopAnchor(button3, 50.0);
        AnchorPane.setLeftAnchor(button3, 50.0);

        // Crear una escena con AnchorPane como root node
        Scene scene = new Scene(anchorPane, 300, 200);

        // Configurar el escenario
        stage.setTitle("AnchorPane Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

Uno de los usos que se le da a este panel es anclar un nodo a múltiples bordes para que se estire cuando el tamaño del contenedor cambie. Veámoslo en un ejemplo:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        Button button = new Button("Stretch");

        AnchorPane anchorPane = new AnchorPane();

        anchorPane.getChildren().add(button);

        AnchorPane.setTopAnchor(button, 10.0);
        AnchorPane.setBottomAnchor(button, 10.0);
        AnchorPane.setLeftAnchor(button, 10.0);
        AnchorPane.setRightAnchor(button, 10.0);

        Scene scene = new Scene(anchorPane, 300, 200);

        stage.setTitle("Advanced AnchorPane Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

TilePane

TilePane es un contenedor de diseño en JavaFX que organiza sus nodos hijos en una cuadrícula de celdas de tamaño uniforme. Es útil cuando deseas disponer componentes en filas y columnas de manera uniforme, sin preocuparte demasiado por el tamaño de cada celda, ya que todas tendrán el mismo tamaño. Con setOrientation(Orientation o) se puede establecer la orientación del panel.

TilePane

Figura 11 - TilePane

Se puede indicar el tamaño preferido por las celdas utilizando los métodos setPrefTileWidth(double value) y setPrefTileHeight(double value). Veamos un ejemplo:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        Button button1 = new Button("Button 1");
        Button button2 = new Button("Button 2");
        Button button3 = new Button("Button 3");
        Button button4 = new Button("Button 4");
        Button button5 = new Button("Button 5");
        Button button6 = new Button("Button 6");

        TilePane tilePane = new TilePane(10, 10);
        tilePane.setOrientation(Orientation.HORIZONTAL);
        tilePane.setPrefTileWidth(100);
        tilePane.setPrefTileHeight(50);
        tilePane.getChildren().addAll(button1, button2, button3, button4, button5, button6);

        Scene scene = new Scene(tilePane, 400, 200);

        stage.setTitle("Complete TilePane Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

BorderPane

BorderPane es un contenedor de diseño en JavaFX que organiza sus nodos hijos en cinco regiones distintas: superior (top), inferior (bottom), izquierda (left), derecha (right) y centro (center). Este contenedor es útil para crear interfaces de usuario que requieren una disposición estructurada y equilibrada.

Las regiones superiores e inferiores ocupan todo el ancho posible del padre pero la altura que necesite. Mientras que, las regiones izquierda y derecha, ocupan todo el alto posible y la anchura necesaria.

BorderPane

Figura 12 - BorderPane

Haciendo uso de los métodos setXXX(Node n), siendo XXX la posición, se puede posicionar los elementos en la parte deseada.

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage)  {
        Button topButton = new Button("Top");
        Button bottomButton = new Button("Bottom");
        Button leftButton = new Button("Left");
        Button rightButton = new Button("Right");
        Button centerButton = new Button("Center");

        BorderPane borderPane = new BorderPane();

        borderPane.setTop(topButton);
        borderPane.setBottom(bottomButton);
        borderPane.setLeft(leftButton);
        borderPane.setRight(rightButton);
        borderPane.setCenter(centerButton);

        Scene scene = new Scene(borderPane, 300, 200);

        stage.setTitle("BorderPane Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

GridPane

GridPane es un contenedor de diseño en JavaFX que organiza sus nodos hijos en una cuadrícula flexible de filas y columnas. Es especialmente útil para crear interfaces de usuario con una disposición tabular, donde puedes controlar la alineación, el espaciado y el tamaño de las celdas.

GridPane

Figura 13 - GridPane

Para añadir un elemento al contenedor se utiliza el método add(Node n, int columnIndex, int rowIndex), el cuál recibe el elemento a añadir y la celda en la cuál se desea añadir. Veamos un ejemplo básico:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        Button button1 = new Button("Button 1");
        Button button2 = new Button("Button 2");
        Button button3 = new Button("Button 3");
        Button button4 = new Button("Button 4");

        GridPane gridPane = new GridPane();

        gridPane.add(button1, 0, 0); // Columna 0, Fila 0
        gridPane.add(button2, 1, 0); // Columna 1, Fila 0
        gridPane.add(button3, 0, 1); // Columna 0, Fila 1
        gridPane.add(button4, 1, 1); // Columna 1, Fila 1

        Scene scene = new Scene(gridPane, 300, 200);

        stage.setTitle("GridPane Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

Existe un constructor para indicar el espaciado horizontal y vertical, GridPane(double hgap, double vgap), así como los métodos setters setHgap(double d) y setVgap(double d).

El método para añadir elementos está sobrescrito para recibir más parámetros, add(Node n, int columnIndex, int rowIndex, int columnSpan, int rowSpan) donde columnSpan y rowSpan son el número de columnas y filas a unificar.

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        Button button1 = new Button("Button 1");
        Button button2 = new Button("Button 2");
        Button button3 = new Button("Button 3");

        GridPane gridPane = new GridPane(10, 10);

        gridPane.add(button1, 0, 0);
        gridPane.add(button2, 1, 0);
        gridPane.add(button3, 0, 1, 1, 2);

        Scene scene = new Scene(gridPane, 300, 200);

        stage.setTitle("GridPane with unified cells example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

Con la clase RowConstraints podemos modificar el tamaño de la filas. Los métodos de esta clase son:

  • setPrefHeight(): Establece la altura preferida de la fila.
  • setMinHeight(): Establece la altura mínima.
  • setMaxHeight(): Establece la altura máxima.
  • setPercentHeight(): Establece la altura de la fila como un porcentaje de la altura total del GridPane.
  • setVgrow(Priority priority): Establece la prioridad de crecimiento vertical de la fila.
  • setFillHeight(boolean b): Especifica si las celdas de una fila deben llenar todo el espacio vertical disponible. Cuando este método se establece en true, las celdas de la fila llenarán todo el espacio disponible en la fila, ajustando la altura de los nodos hijos para que coincidan con la altura de la fila. Si se establece en false, las celdas mantendrán su altura preferida y no llenarán el espacio vertical adicional.

También existe la clase ColumnConstraints donde se estable el tamaño de las columnas. Los métodos son iguales que los anteriores, pero en lugar de usar la altura (height) se usa la anchura (width).

Para añadir estas constraints es necesario obtener la constraint correspondiente al GridPane, a través de los métodos getRowsConstraints() y getColumnsConstraints(), y una vez obtenidas, se añade a través de los métodos add() o addAll(). Veamos un ejemplo:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        Button button1 = new Button("Button 1");
        Button button2 = new Button("Button 2");
        Button button3 = new Button("Button 3");
        Button button4 = new Button("Button 4");

        GridPane gridPane = new GridPane();
        gridPane.setHgap(10);
        gridPane.setVgap(10);
        gridPane.setPadding(new Insets(10));

        ColumnConstraints col1 = new ColumnConstraints();
        col1.setPercentWidth(50);
        ColumnConstraints col2 = new ColumnConstraints();
        col2.setPercentWidth(50);

        gridPane.getColumnConstraints().addAll(col1, col2);

        RowConstraints row1 = new RowConstraints();
        row1.setPrefHeight(100);
        row1.setVgrow(Priority.ALWAYS);
        row1.setFillHeight(true);  

        RowConstraints row2 = new RowConstraints();
        row2.setPrefHeight(100);
        row2.setVgrow(Priority.NEVER);
        row2.setFillHeight(false);  

        gridPane.getRowConstraints().addAll(row1, row2);

        gridPane.add(button1, 0, 0); // Columna 0, Fila 0
        gridPane.add(button2, 1, 0); // Columna 1, Fila 0
        gridPane.add(button3, 0, 1); // Columna 0, Fila 1
        gridPane.add(button4, 1, 1); // Columna 1, Fila 1

        Scene scene = new Scene(gridPane, 300, 200);

        stage.setTitle("GridPane Constraints Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

ScrollPane

ScrollPane en JavaFX es un contenedor que proporciona funcionalidad de desplazamiento para su contenido. Es útil cuando el contenido dentro del área visible del contenedor es más grande que el tamaño del contenedor mismo, permitiendo al usuario desplazarse para ver el contenido completo.

A través de los métodos setHbarPolicy(ScrollBarPolicy policy) y setVbarPolicy(ScrollBarPolicy policy) establece la política de barras de desplazamiento horizontal y vertical respectivamente. Las opciones disponibles son:

  • ScrollBarPolicy.ALWAYS: siempre se muestran
  • ScrollBarPolicy.AS_NEEDED: solo se muestra cuando el contenido lo require. Este es el valor por defecto.
  • ScrollBarPolicy.NEVER: no se muestra nunca.

Los métodos setFitToWidth(boolean value) y setFitToHeight(boolean value) establecen si el ScrollPane debe ajustarse automáticamente al ancho y/o alto de su contenido.

A través del método setContent(Node n) se indica el contenido a mostrar. Normalmente, se suele utilizar alguno de los layouts vistos anteriormente dentro de este tipo de layout. Veamos un ejemplo:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {

        Label content = new Label("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
                "Nullam et metus ut nisi semper fermentum eget nec elit. " +
                "Sed pharetra volutpat lectus non molestie. " +
                "Duis consequat nisi sit amet sem mattis, ac tempor purus gravida. " +
                "Nunc dignissim feugiat urna, sed convallis arcu ultricies vel. " +
                "Proin ut neque nulla. Sed sit amet varius metus, ac placerat libero.");

        ScrollPane scrollPane = new ScrollPane();
        scrollPane.setContent(content);

        scrollPane.setPrefSize(300, 200);

        StackPane root = new StackPane();
        root.getChildren().add(scrollPane);
        Scene scene = new Scene(root, 400, 300);

        stage.setTitle("ScrollPane Example");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}