BoxLayout

Ramiro Lago (Octubre 2005)

Introducción

Un tutorial sobre este interesante Layout de Swing.

Vamos examinar este Layout de Swing a través de un ejemplo en el que construiremos un formulario. Empezaremos con algo sencillo: hemos usado setLayout(...) para asignarle al contenedor raíz el BoxLyout. Obtenemos el tamaño del applet para visualizarlo en los JTextField. No hemos especificado tamaño para los componentes, a pesar de ello, los JLabel y los JButton adoptan el tamaño adecuado para que se muestre su texto. A continuación una imagen del applet:

El resultado no es precisamente vistoso. Su código:


package boxlayout_applet;

import java.awt.*;
import javax.swing.*;

public class MiApplet extends JApplet {
   //// Etiquetas (justificado a la izquierda), textos y botones
   JLabel etiAncho = new JLabel( "Ancho:", SwingConstants.LEFT );
   JTextField txtAncho = new JTextField();
   JLabel etiAlto = new JLabel( "Alto:", SwingConstants.LEFT );
   JTextField txtAlto = new JTextField();
   JButton btnBoton1 = new JButton( "Botón 1" );

   //Initialize the applet
   public void init() {
      try {
	 //// Obtenemos tamaño del applet
	 int anchoApplet = this.getWidth();
	 int altoApplet = this.getHeight();

	 //// Contenedor raiz: BoxLayout
	 Container contRaiz = getContentPane();
	 contRaiz.setLayout( new BoxLayout( contRaiz, BoxLayout.PAGE_AXIS));
	 contRaiz.setBackground( Color.cyan);

	 //// Introduzco ancho y alto de applet en los campos
	 txtAncho.setText( String.valueOf( anchoApplet));
	 txtAlto.setText( String.valueOf( altoApplet));

	 //// Añado subpaneles y botón al panel raiz
	 contRaiz.add( etiAncho );
	 contRaiz.add( txtAncho );
	 contRaiz.add( etiAlto );
	 contRaiz.add( txtAlto );
	 contRaiz.add( btnBoton1 );
      }
      catch(Exception e) {
	 e.printStackTrace();
      }
   }
}

Observar que el constructor del BoxLayout exige dos argumentos, el primero es el contenedor al que se asigna. El segundo es la orientación general:

Dos ideas para empezar:

El siguiente paso será:

El aspecto resultante es:

Refinando el ejemplo

Ha mejorado, pero todavía podemos refinarlo para dar un aspecto de formulario, donde las etiquetas aparezcan a la izquierda y en la misma línea que su correspondiente campo de texto. Para ello nos ayudaremos de subpaneles. Dejar un interfaz adecuado no depende sólo de conocer los Layouts, también es necesario anidar paneles. En nuestro caso, haremos que cada par JLabel-JTextField este contenido en un subpanel. Por ejemplo, etiAncho y txtAncho se añaden a un subpanel denominado pnlAncho, que justifica sus componentes a la izquierda:


         //// Añado etiquetas y campos a subpaneles
         JPanel pnlAncho = new JPanel();            // Subpanel del ancho
	 pnlAncho.setLayout( new FlowLayout(FlowLayout.LEFT));
	 pnlAncho.add( etiAncho );
	 pnlAncho.add( txtAncho );

Además, para que todas las etiquetas tengan el mismo ancho les aplico setTamaño(). El código de init() ha quedado así:


   public void init() {
      try {
	 //// Obtenemos tamaño del applet
	 int anchoApplet = this.getWidth();
	 int altoApplet = this.getHeight();

	 //// Contenedor raiz: BoxLayout
	 Container contRaiz = getContentPane();
	 contRaiz.setLayout( new BoxLayout( contRaiz, BoxLayout.PAGE_AXIS));
	 contRaiz.setBackground( Color.cyan);

	 //// Introduzco ancho y alto de applet en los campos
	 txtAncho.setText( String.valueOf( anchoApplet));
	 txtAlto.setText( String.valueOf( altoApplet));

	 //// El texto se alinea a la derecha
	 txtAncho.setHorizontalAlignment( JTextField.RIGHT);
	 txtAlto.setHorizontalAlignment( JTextField.RIGHT);

	 //// Indico tamaño
	 setTamaño( etiAncho, 40,20);
	 setTamaño( txtAncho, 80,20);
	 setTamaño( etiAlto, 40,20);
	 setTamaño( txtAlto, 80,20);

         //// Añado etiquetas y campos a subpaneles
         JPanel pnlAncho = new JPanel();            // Subpanel del ancho
	 pnlAncho.setLayout( new FlowLayout(FlowLayout.LEFT));
	 pnlAncho.setBackground( Color.BLUE);
	 pnlAncho.add( etiAncho );
	 pnlAncho.add( txtAncho );
	 JPanel pnlAlto = new JPanel();             // Subpanel de la altura
	 pnlAlto.setLayout( new FlowLayout(FlowLayout.LEFT));
	 pnlAlto.setBackground( Color.GREEN);
	 pnlAlto.add( etiAlto );
	 pnlAlto.add( txtAlto );

	 btnBoton1.setAlignmentX( Component.CENTER_ALIGNMENT );

	 //// Añado subpaneles y botón al panel raiz
	 contRaiz.add( pnlAncho );
	 contRaiz.add( pnlAlto );
	 contRaiz.add( Box.createVerticalGlue());
	 contRaiz.add( btnBoton1 );
      }
      catch(Exception e) {
	 e.printStackTrace();
      }
   }

Observar que el botón lo hemos alineado hacia el centro, por medio de setAlignmentX( Component.CENTER_ALIGNMENT )con lo que aunque hagamos más ancho el applet siempre se mantendrá centrado. La imagen del resultado es la siguiente:

Podemos observar que nos volvemos a encontrar con el fenomeno de expansión automática: no hemos determinado el tamaño de los subpaneles, con lo que se expanden sobre el panel raiz, ocupando más altura de la necesaria. Esto tiene una solución sencilla con setPreferredSize(). Esta forma de determinar tamaño es normalmente flexible: el subpanel adapta su tamaño al aumento del tamaño del panel padre:


	 pnlAncho.setPreferredSize( new Dimension( anchoApplet, 24));
	 pnlAlto.setPreferredSize( new Dimension( anchoApplet, 24));

Por último, Box.createRigidArea()

Existe otra forma de componente invisible que puede ser de utilidad: Box.createRigidArea(new Dimension(ancho, alto)). Este método inserta un componente invisible con el tamaño especificado. Nos permite separar componentes o subpaneles de un BoxLayout. Si a nuestro ejemplo anterior le añadimos un segundo y tercer botón, aparecerán juntos. Gracias a este método tienen un espacio de saparación:


	 contRaiz.add( btnBoton1 );
	 contRaiz.add( Box.createRigidArea(new Dimension(3,3) ));
	 contRaiz.add( btnBoton2 );
	 contRaiz.add( Box.createRigidArea(new Dimension(3,3) ));
	 contRaiz.add( btnBoton3 );

El resultado final aparece a continuación. No es un error, se ha hecho a proposito: el botón 3 no tiene espacio suficiente. Los componentes que no caben no aparecen:

Código fuente
Volver al índice