## Cómo usar BoxLayout

Los paquetes Swing incluyen un controlador de distribución de propósito geneal llamado BoxLayout. BoxLayout o bien situa los componenes uno encima d eotro (con el primer componente en la parte superior) o los situa en una delgada fila de izquierda a derecha - a nuestra eleción. Podríamos pensar que es una versión más potente de FlowLayout. Aquí tenemos una versión de un applet que usa un BoxLayout para mostrar una columna de componetes centrada.

Esta es una imagen del GUI del applet, para ejecutarlo, pulsa sobre ella y el applet aparecerá en una nueva ventana del navegador.

Creando uno o más contenedores de peso ligero que usen BoxLayout, podemos conseguir distribuciones más complejas para las que normalmente se usaría GridBagLayout. BoxLayout también es útil en algunas situaciones donde podríamos considerar la utilización de GridLayout o BorderLayout. Hay una gran diferencia entre BoxLayout y los controladores de distribución existentes en el AWT, y es que BoxLayout respeta el tamaño máximo y el alineamiento X/Y de cada componente.

La siguiente figura muestra un GUI que usa dos ejemplares de BoxLayout. En la parte superior del GUI un boxlayout de arriba-a-abajo situa una etiqueta sobre un scroll pane. En la parte inferior del GUI, un boclayout de izquierda-a-derecha situa dos botones uno junto al otro. Un BorderLayout combina las dos partes del GUI y se asegura que cualquier exceso de espacio será entregado al scroll pane.

El siguiente código, tomado de ListDialog.java, disribuye el GUI. Este código está en el constructor del diálogo, que está implementadocomo una subclase de JDialog. Las líneas en negrita inicializan los boxlayout y les añaden los componentes.

    JScrollPane listScroller = new JScrollPane(list);
    listScroller.setPreferredSize(new Dimension(250, 80));
    listScroller.setMinimumSize(new Dimension(250, 80));
    listScroller.setAlignmentX(LEFT_ALIGNMENT);
    ...
    //Lay out the label and scroll pane from top to bottom.
    JPanel listPane = new JPanel();
    listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
    JLabel label = new JLabel(labelText);
    listPane.add(label);
    listPane.add(Box.createRigidArea(new Dimension(0,5)));
    listPane.add(listScroller);
    listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));

    //Lay out the buttons from left to LEFT.
    JPanel buttonPane = new JPanel();
    buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
    buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
    buttonPane.add(Box.createHorizontalGlue());
    buttonPane.add(cancelButton);
    buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
    buttonPane.add(setButton);

    //Put everything together, using the content pane's BorderLayout.
    Container contentPane = getContentPane();
    contentPane.add(listPane, BorderLayout.CENTER);
    contentPane.add(buttonPane, BorderLayout.SOUTH);

La primera línea en negrita crea el boxlayout de ariba-a-abajo y lo inicializada como el controlador de distribución para el listPane. Los dos argumentos del constructor de BoxLayout son el contenedor que maneja y el eje sobre el que se van a distribuir los componente. Las tres siguiente líneas en negrita añaden la etiqueta y el scroll pane al contenedor, separándolas con un área rígida -- un componente invisible de peso ligero usado para añadir espacio entre componentes. En este caso, el área rígida no tiene anchura y pone exactamente 5 pixels entre la etiqueta y el scroll pane. Las áreas rígidas se describen más tarde en Usar componentes invisibles como relleno.

El siguiente bloque de código en negrita crea un boxlyout de izquierda-a-derecha y lo selecciona para contenedor buttonPane. Luego el código añade dos botones al contenedor, usando un área rígida de 10 pixels entre los botones. Para hacer que los botones sean situadas en el lado derecho de su contenedor, el primer componente añadido al contenedor es glue. Este glue es un componente invisible de peso ligero que crece lo necesario para absrover cualquier espacio extra en su contenedor. Glue se describe en Usar componentes invisibles como relleno.

Como alternativa al uso de componentes invisibles, algunas veces podemos usar bordes vacíos para crear espacio alrededor de los componentes. Por ejemplo, el código anterior usa bordes vacíos de 10 pixels entre todos los lados del dialog y sus contenidos, y entre las dos partes de los contenidos. Los bordes son completamente independientes de los controladores de distribución. Son sólo la forma en que los componentes Swing dibujan sus lados. Para más información puedes ver Cómo usar Borders.

Las siguientes secciones explican BoxLayout en más detalle:

- Caracterísitcas de Boxlayout
- Usar componentes invisibles como relleno
- Resolver problemas de alineamiento
- Especificar tamaños de componentes
- El API de Boxlayout
- Ejemplos que usan Boxlayout

No dejes que te asuste la longitud de esta página. Probablemente podrás usar BoxLayout con la información que ua posees. Si tienen problemas o quieres tomar ventaja de la potencia de BoxLayout puedes leerlo más tarde.

#### Características de BoxLayout

> Como hemos dicho antes, un BoxLayout dsiribuye componentes de arriba-a-abajo o de ozquierda-a-derecha. Y esta distribución tiene en cuentra los alineamientos y los tamaño mínimo preferido y máximo de caa componente. En esta sección, hablaremos sobre la distribución de arriba-a-abajo (eje Y). Los mismos conceptos se pueden aplicar a la distribución de izquierda a derecha. Simplemente tenemos que sustituir X por Y, anchura por altura, etc.

> Cuando un BoxLayout distribuye sus componentes de arriba a abajo, intenta dismiensionarlos a la altura preferida de cada uno de ello. Si la cantidad de espacio vertical no es ideal, el boxlayout intenta ajustar la altura de cada componente para quue los componente llener la cantidad de espacio disponible. Si embargo, los componentes podrían no caber exactamente, ya que BoxLayout respeta las alturas mínima y máxima de cada componente. Cualquier espacio extra aparecerá en la parte inferior del contenedor.

> Un BoxLayout de arriba-a-abajo intenta hacer que todos sus componentes tengan la misma anchura -- tan anchos como la anchura máxima preferida. Si se fuerza el contenedor a ser más ancho que esto, el boxlayout intenta hacer todos los componentes tan anchos como el contenedor. Si los componentes no son de la misma anchura (debido a las restricciones del tamaño máximo o a que alguno de ellos tiene un alineamiento estricto a la izquierda a derecha), el alineamiento X entra en juego.

> El alineamiento X afecta no sólo a la posición relativa de los componentes, sino también a la localización de los componentes (como grupo) dentro del contenedor. Las siguiente figuras ilustran el alineamiento de componentes que tienen anchuras máximas restringidas.

> En la primera figura, los tres componentes tienen un alineamiento X de 0.0 (Component.LEFT_ALIGNMENT). Esto significa que los lados izquierdos de los componentes deberían estar alineados. Además, significa que los tres componentes deben posicionarse tan a la izquierda de su contenedor como sea posible.

> En la segunda figura, los tres componentes tiene un alineamiento X de 0.5 (Component.CENTER_ALIGNMENT). Esto significa que los centros de los componentes deben estar alineados, y que los contenedores deberían situarse en el centro horizontal de su contenedor.

> En la tercera figura, los componentes tienen un alineamiento X de 1.0 (Component.LEFT_ALIGNMENT). Puedes imaginar lo que esto significa para el alineamiento de los componetes y para su posición relativa dentro del contenedor.

> Podríamos preocuparnos por lo que sucederá cuando los tienen tamaños máximo restringidos y diferentes alineamientos X. La siguiente figura muestra un ejemplo de esto:

> Como puedes ver, el lado izquierdeo con alineamiento X de 0.0 (Component.LEFT_ALIGNMENT) está alineado con el centro del componente con el alineamiento X de 0.5 (Component.CENTER_ALIGNMENT), que está alineado con el lado derecho del componente con el alineamiento X de 1.0 (Component.LEFT_ALIGNMENT). Los alineamientos mezclados como esto se discuten más adelante en Resolver problemas de Alineamiento.

> ¿Qué pasa si ningún componente tiene una anchura máxima? Bien, si todos los componentes tienen idéntico alineamiento X, todos los componentes serán tan anchos como su contenedor. Si el alineamiento X es diferente, cualquier componente con alineamiento 0.0 (izquierda) o 1.0 (derecha) serán más pequeños. Todos los componentes con una alineamiento X intermedio (como el centro) serán tan anchos como su contenedor. Aquí tenemos dos ejemplos:

> Para conocer mejor BoxLayout, puedes hacer tus propios experimentos con BoxLayoutDemo.

> > Prueba esto:
> > 1. Compila y ejecuta BoxLayoutDemo. Los ficheros fuentes son BoxLayoutDemo.java y BLDComponent.java.
> > 2. Pulsa dentro de uno de los rectángulos
> >    Así es como se cambia el alineamiento X de los rectángulos.
> > 3. Pulsa el check box de la parte inferior de la ventana.
> >    Esta desactiva las restriciones de tamaño de todos los rectángulos.
> > 4. Agranda la ventana.
> >    Esto hace que el contenedor de los rectángulos sea más grande que la suma de los tamaño máximo preferidos de los rectángulos. El contenedor en un JPanel que tiene un borde rojo, por lo que puedes diferenciar donde están los laterales del contenedor.

#### Usar Componentes Invisibles como Relleno

> Cada componente controlador por un BoxLayout se pega a sus vecinos. Si queremos tener espacio entre componentes podemos añadir un borde vacío a uno de los componentes o insertar componentes invisibles para proporcionar espacio. Podemos crear componentes invisibles con la ayuda de la clase Box.

> La clase Box porporciona una clase interna Box.Filler que proporciona componentes invisibles. La clase Box proporciona métodos de conveniencia para crear los tipos más comunes de rellenos. La siguiente tabla ofrece detalles sobre la creacción de componentes invisibles con Box y Box.Filler.

> Tipo | Restricciones de tamaño | Cómo crearlo
> --- | --- | ---
> área rígida | XXXXX XXXXX XXXXX |
>     Box.createRigidArea(size)
> glue | horizontal |
>     <- . ->
>     Box.createHorizontalGlue()
> vertical |
>        ^
>        |

>        .

>        |
>        v
>     Box.createVerticalGlue()
> Box.Filler personalizado | (segúns e especifique) |
>     new Box.Filler(minSize, prefSize, maxSize)

> Aquí podemos ver como se usa generalmente cada tipo de relleno:

> Área Rígida
>   Se usa cuando queremos un espacio de tamaño fijo entro dos componentes. Por ejmplo, para poner 5 pixels entre dos componentes en un box de izquierda-a-derecha podemos usar este código

>       container.add(firstComponent);
>       container.add(Box.createRigidArea(new Dimension(5,0)));
>       container.add(secondComponent);

>       Without rigid area:        With rigid area:
>       -------------------        -------------------
>       |XXXXYYY          |        |XXXX     YYY     |
>       |XXXXYYY          |        |XXXX<--->YYY     |
>       |XXXXYYY          |        |XXXX     YYY     |
>       -------------------        -------------------

>   Nota: La clase Box proporciona otro tipo de relleno para poner espacio fijo entre componentes: (struts) un puntal vertical u horizontal. Desafortunadamente los puntales no tienen sus anchuras o alturas máximas (para puntales horizontales o verticales, respectivamente). Esto significa que si usamos un box horizontal dentro de un box vertical por ejemplo, el el box horizontal puede algunas veces convertirse demasiado alto. Por esta razón, recomendamos usar áreas rígidas en vez de "struts".

> Glue
>   Se usa para especificar donde debería ir el exceso de espacio en la distribución. Piensa en ello como un goma semi-humeda -- extrechable y expandible, que sólo toma el espacio que los demás componentes no utilizan. Por ejemplo, ponendo un glue horizontal entre dos componentes en un box de izquierda-a-derecha, podemos hacer que todo el espacio extra se quede entre los dos componentes en vez de a la derecha del contenedor. Aquí puedes ver un ejemplo de ello:

>       container.add(firstComponent);
>       container.add(Box.createHorizontalGlue());
>       container.add(secondComponent);

>       Without glue:              With glue:
>       -------------------        -------------------
>       |XXXXYYY          |        |XXXX          YYY|
>       |XXXXYYY          |        |XXXX<-------->YYY|
>       |XXXXYYY          |        |XXXX          YYY|
>       -------------------        -------------------

> Box.Filler personalizado
>   Usado para especificar un componente con los tamaños mínimo, preeferido y máximo que querramos. Por ejemplo, para crear algún relleno en un boxlayout de izquierda-a-derecha que ponga al menos 5 pixels entre dos componentes y se asegure de que el contenedor tenga una altura mínima de 100 pixels, podríamos usar este código:

>       container.add(firstComponent);
>       Dimension minSize = new Dimension(5, 100);
>       Dimension prefSize = new Dimension(5, 100);
>       Dimension maxSize = new Dimension(Short.MAX_VALUE, 100);
>       container.add(new Box.Filler(minSize, prefSize, maxSize));
>       container.add(secondComponent);

>       Without filler:            With filler:
>       -------------------        -------------------
>       |XXXXYYY          |        |        ^        |
>       |XXXXYYY          |        |XXXX    |     YYY|
>       |XXXXYYY          |        |XXXX<-------->YYY|
>       -------------------        |XXXX    |     YYY|
>                                  |        v        |
>                                  -------------------

#### Resolver Problemas de Alineamiento

> Algunas veces pueden ocurrir dos tipos de problema de alineamiento con BoxLayout:
> - Un grupo de componentes que tienen el mismo alineamiento, pero queremos cambiar sus alineamientos para que se vean mejor. Por ejemplo, en lugar de tener la parte superior de un grupo de botones de izquierda-a-derecha en un línea, podríamos querer alinear la parte inferior de los botones. Aquí tenemos un ejeplo:

> - Dos o más componentes controlados por un BoxLayout tienen distinto alineamiento por defecto, lo que hace que estén desalineados. Por ejemplo, como muestra la siguiente imagen, si una etiqueta y un contenedor están en un boxlayout de arriba-a-abajo, el lado izquierda de la etiqueta, por defecto, está alineado con el centro del contenedor.

>   De forma similar, si un botón y un contenedor están en un boxlayout de izquierda-a-derecha el lado superior del botón, por defecto, está alineado con el centro del contenedor.

> En general, todos los componentes controlados por un objeto BoxLayout de arriba-a-abajo deberían tener el mismo alineamineto X. Similarmente, todos los componetes controlados por un Boxlayout de izquierda-a-derecha deberían tener el mismo alineamiento Y. Podemos seleccionar el alineamiento X de un componente Swing llamando a su método setAlignmentX. Una alternativa es sobreescribir el método getAlignmentX en un subclase personalizada de la clase del componente. Se puede hacer lo mismo para el alineamiento Y con los métodos setAlignmentY y getAlignmentY.

> Aquí hay un ejemplo, tomado de AlignmentDemo.java, de cambio de alineamiento Y de dos botones para la parte inferior de los botones esté alineada:

>     button1.setAlignmentY(Component.BOTTOM_ALIGNMENT);
>     button2.setAlignmentY(Component.BOTTOM_ALIGNMENT);

> Como muestra la siguiente tabla, los botones, las etiquetas y los items de menú Swing tienen diferentes valores de alineamiento X que todos los demás componentes. De igual forma, los botones, los items de menú y las barras de herraientas tienen diferentes alineamientis Y que todos los demás componentes.

> Componente Swing | Alineamiento X por defecto | Alineamineto Y por defecto
> --- | --- | ---
> Buttons, menu items | LEFT_ALIGNMENT | TOP_ALIGNMENT
> Labels | LEFT_ALIGNMENT | CENTER_ALIGNMENT
> Tool bars | CENTER_ALIGNMENT | TOP_ALIGNMENT
> Todos los demás componentes Swing | CENTER_ALIGNMENT | CENTER_ALIGNMENT

> El programa AlignmentDemo.java ofrece ejemplos de cómo corregir los problemas de alineamientos. Normalmente es tan senciilo como forzar a los botones y las etiquetas a que tengan alinamiento centrado. Por ejemplo:

>     label.setAlignmentX(Component.CENTER_ALIGNMENT);
>     ...
>     button.setAlignmentY(Component.CENTER_ALIGNMENT);

#### Especificar Tamaños de Componentes

> Como mencionamos antes, BoxLayout presta atención a las peticiones de tamaño mínimo, preferido y máximo del componente. Mientras estemos ajustando la distribución, podríamos necesitar modificar estos tamaños.

> Algunas veces la necesidad de ajustar el tamaño es obvia. Por ejemplo, el tamaño máximo de un botón Swing es el mismo que su tamaño preferido. Si queremos que un botón se dibuje más ancho cuando haya espacio disponible, obviamente necesitaremos cambiar el tamaño máximo.

> Sin embargo, alguna veces, la necesidad de ajustar el tamaño no es tan obvia. Podríamos obtener resultados inesperdados con un boxlayout, y querrámos saber por qué. En este caso, lo mejor es tratar el problema como si fuera un problema de alineamiento en primer lugar. Si ajustar los alineamientos no ayuda, podríamos tener un problema de tamaños. Lo discutiremos un poco más tarde.

> > Nota: aunque BoxLayout presta atención al tamaño máximo de un componente, la mayoría de los controladores de distribución no lo hacen. Por ejemplo, su ponemos un botón en la parte SOUTH de un BorderLayout, el botón probablemente será más ancho que su tamaño preferido. Por otro lado BoxLayout, nunca hace un botón tan ancho como su tamaño preferido a menos que especificamente cambiemos su tamaño máximo.

> Podemos cambiar los tamaños mínimo, preferido y máximo de dos formas:

> - Llamando al método setXxxSize apropiado (que está definido por la clase JComponent). Por ejemplo:

>       comp.setMinimumSize(new Dimension(50, 25));
>       comp.setPreferredSize(new Dimension(50, 25));
>       comp.setMaximumSize(new Dimension(Short.MAX_VALUE,
>                                         Short.MAX_VALUE));

> - Sobreescribiendo el método getXxxSize apropiado. Por ejemplo:

>       ...//in a subclass of a Swing component class:
>       public void getMaximumSize() {
>           size = getPreferredSize();
>           size.width = Short.MAX_VALUE; //[PENDING: specify Short or Integer?]
>           return size;
>       }

> Si tenemos problemas con un boxlayout y hemos resuelto los problemas de alineamiento, el problema podría estar relacionado con el tamaño. Por ejemplo, si el contenedor controlador por el boxlayout está tomando demasiado espacio, entonces uno o más componentes probablemente necesiten tener su tamaño máximo restringido.

> Podemos usar dos técnicas para seguir la pista de los problemas de tamaño en un boxlayout:

> - Añadir un borde en el lado exterior del componente Swing en cuestión. Esto permitirá ver el tamaño real del componente. Por ejemplo:

>       comp.setBorder(BorderFactory.createCompoundBorder(
>                          BorderFactory.createLineBorder(Color.red),
>                          comp.getBorder()));

> - Usar el viejo System.out.println para imprimir los tamaños mínimo, preferido y máximo del componente y quizás sus límites.

#### El API de BoxLayout

> Las siguiente tablas listan los métodos y constructores más usados de BoxLayout y Box. El API para usar BoxLayut se divide en tres categorías:

> - Crear objetos BoxLayout
> - Crear rellenos
> - Otros métodos útiles

> Crear objetos BoxLayout
> Método o Constructor | Propósito
> --- | ---
> BoxLayout(Container, int) | Crea un ejemplar de BoxLayout que controla el Container especificado. El argumento entero especifica si los componentes deberían distribuirse de izquierda-a-derecha (BoxLayout.X_AXIS) o de arriba-a-abajo (BoxLayout.Y_AXIS).
> Box(int) | Crea un Box -- un contenedor de peso ligero que usa BoxLayout con el alineamiento especificado (BoxLayout.X_AXIS o BoxLayout.Y_AXIS). Observa que un Box no es un JComponent -- está implementado como una subclase de Container. Esto hace que sea tan ligero como sea posible, pero se pierde algunas características de JComponent como los bordes. Si queremios un sencillo JComponent como contenedor, debemos usar JPanel.
> static Box createHorizontalBox() | Crea un Box que distribuye sus componentes de izquierda-a-derecha.
> static Box createVerticalBox() | Crea un Box que distribuye sus componentes de arriba-a-abajo.

> Crear rellenos
> Método o Constructor | Propósito
> --- | ---
> Component createHorizontalGlue() Component createVerticalGlue() Component createGlue() | Crea un componente de peso ligero glue.
> Component createHorizontalStrut() Component createVerticalStrut() | Crea componente de peso ligero "strut" Nosotros recomendamos el uso de áreas rígidas en vez de los struts.
> Box.Filler(Dimension, Dimension, Dimension) | Crea un componente de peso ligero con los tamaños mínimo, preferido y máximo especificados (con los argumentos especificados en este orden).

> Otros métodos útiles
> Método | Propósito
> --- | ---
> void changeShape(Dimension, Dimension, Dimension) (en Box.Filler) | Cambia los tamaños mínimo, preferido u máximo del recipiente del objeto Box.Filler. La distribución cambiara de forma automática.

#### Ejemplos que usan BoxLayouts

> La siguiente tabla lista algunos de los muchos ejemplos que usan boxlayout:

> Ejemplo | Dónde se describe | Notas
> --- | --- | ---
> BoxWindow.java | Esta página | Usa un boxlayout para crear una columna de componentes centrados.
> AlignmentDemo.java | Esta página | Demuestra como corregir los problemas más comunes de alineamiento. Usa dos ficheros de imagen: images/middle.gif y images/geek-cght.gif.
> BoxLayoutDemo.java | Esta página | Nos permite jugar con los alineamientos y los tamaño máximos.
> ListDialog.java | Esta página | Un sencillo pero realista ejemplo que usa dos boxlayout uno de arriba-a-abajo y otro de izquierda-a-derecha.
> InternalFrameEventDemo.java | Cómo escribir un oyente de Internal Frame | Usa un boxlayout de arriba-a-abajo pra centrar botones y un scroll pane en un frame interno.

Ozito