Tutorial de Java

AWT - Layouts (III)

Anterior | Siguiente

GridLayout

La composición GridLayout proporciona gran flexibilidad para situar Componentes. El controlador de posicionamiento se crea con un determinado número de filas y columnas y los Componentes van dentro de las celdas de la tabla así definida.

Si el Contenedor es alterado en su tamaño en tiempo de ejecución, el sistema intentará mantener el mismo número de filas y columnas dentro de los márgenes de separación que se hayan indicado. En este caso, estos márgenes tienen prioridad sobre el tamaño mínimo que se haya indicado para los Componentes, por lo que puede llegar a conseguirse que sean de un tamaño tan pequeño que sus etiquetas sean ilegibles.

En el ejemplo java1325.java, se muestra el uso de este controlador de posicionamiento de Componentes. Además, el programa tiene un cierto incremento de complejidad respecto a los que se han visto hasta ahora para mostrar layouts, porque en este caso se crea un objeto para el interfaz de usuario que está compuesto a su vez de más objetos de nivel inferior. Y también, se ilustra el proceso de modificar dinámicamente, en tiempo de ejecución, un layout.

El interfaz de usuario está constituido por un objeto Frame sobre el que se colocan objetos Panel utilizando el controlador de posicionamiento por defecto para el Frame, BorderLayout. Uno de los objetos Panel contiene seis objetos Button posicionados utilizando un GridLayout, y no tienen ninguna funcionalidad asignada, no se registra sobre ellos ningún receptor de eventos. Inicialmente, los botones se colocan en una tabla de dos filas y tres columnas.

El otro objeto Panel contiene dos botones con los rótulos 3x2 y 2x3, que se colocan utilizando un FlowLayout. Además, estos botones sí son funcionales, ya que se registran sobre ellos objetos ActionListener. Cuando se pulsa sobre el botón 3x2, los botones del otro Panel se reposicionan en tres filas y dos columnas. Y, de forma semejante, cuando se pulsa sobre el botón 2x3, los objetos Button del otro panel se recolocan en dos filas y tres columnas.

Se instancia y registra, también, un receptor de eventos de cierre de ventana sobre el objeto Frame, para terminar el programa cuando se pulse sobre el botón de cierre del Frame.

import java.awt.*;
import java.awt.event.*;

public class java1325 {
    public static void main( String args[] ) {
        IHM ihm = new IHM();
        }
    }

class IHM {
    public IHM() {
        // Se instancian los dos botones que van a proporcionar la
        // funcionalidad a la aplicacion
        Button boton7 = new Button( "3x2" );
        Button boton8 = new Button( "2x3" );    

        // Se instancia un objeto layout de tipo GridLayout para ser
        // utilizado con el Panel
        GridLayout miGridLayout = new GridLayout( 2,3 );

        // Instancia el primero de los dos objetos Panel que sera
        // integrado en el objeto Frame
        Panel panel1 = new Panel();
        // Fijamos el layout que habiamos definido para el panel
        panel1.setLayout( miGridLayout );
        // Se colocan los seis botones sobre el panel con una
        // etiqueta indicando su numero
        for( int i=0; i < 6; i++)
            panel1.add( new Button( "Boton"+i ) );

        // Se instancia el segundo objeto Panel utilizando el FlowLayout
        // por defecto y se colocan los dos botones funcionales sobre el.
        // A estos botones se les añadira su funcionalidad a traves de
        // objetos receptores de los eventos ActionListener registrados
        // sobre ellos
        Panel panel2 = new Panel();
        panel2.add( boton7 );
        panel2.add( boton8 );  

        // Se instancia el objeto Frame, que sera el padre de todo
        // el interfaz de usuario que se esta creando
        Frame miFrame = new Frame( "Tutorial de Java, AWT" );

        // IMPORTANTE: Se añaden los dos objetos Panel que se han
        // preparado al objeto Frame para crear el interfaz definitivo
        miFrame.add( panel1,"North" );
        miFrame.add( panel2,"South" );

        miFrame.setSize( 250,150 );
        miFrame.setVisible( true );

        // Se instancian los objetos Receptores de eventos Action y se
        // registran con los botones 7 y 8, que corresponden al
        // segundo Panel y que van a modificar el posicionamiento de
        // los otrs seis botones en el Panel contiguo
        boton7.addActionListener( 
            new A3x2ActionListener( miGridLayout,miFrame ) );
        boton8.addActionListener( 
            new A2x3ActionListener( miGridLayout,miFrame ) );

        // Se termina de dar funcionalidad al interfaz, instanciando y
        // registrando un objeto receptor de eventos de la ventana para
        // concluir la ejecucion de la aplicacion cuando el usuario cierre
        // el Frame    
        miFrame.addWindowListener( new Conclusion() );
        }
    }

// Las siguientes dos clases son las clases de los ActionListener, 
// los receptores de eventos de tipo Action. Un objeto de cada una
// de ellas se instancia y registra sobore cada uno de los dos
// botones funcionales de la aplicacion. El proposito de estos
// controladores de eventos es modificar el layout del panel
// contiguo, de forma que los botones que estan colocados sobre
// el se cambien de posicion
class A3x2ActionListener implements ActionListener {
    GridLayout miObjGridLayout;
    Frame miObjFrame;
  
    A3x2ActionListener( GridLayout layout,Frame frame ) {
        miObjGridLayout = layout;
        miObjFrame = frame;
        }

    // Cuando se produce un evento Action, se fijan las filas a 3 y
    // la columnas a 2 sobre el objeto GridLayout. Luego se fija el
    // controlador de posicionamiento para que sea el nuevo que
    // acabamos de modificar, y posteriormente se valida el Frame 
    // para asegurarse de que el alyout es valido y tendra efecto
    // sobre la visualizacion en pantalla
    public void actionPerformed( ActionEvent evt ) {
        miObjGridLayout.setRows( 3 );
        miObjGridLayout.setColumns( 2 );
        miObjFrame.setLayout( miObjGridLayout );
        miObjFrame.validate();
        }
    }

class A2x3ActionListener implements ActionListener {
    GridLayout miObjGridLayout;
    Frame miObjFrame;

    A2x3ActionListener( GridLayout layout,Frame frame ) {
        miObjGridLayout = layout;
        miObjFrame = frame;
        }

    // Cuando se produce un evento Action, se fijan las filas a 2 y
    // la columnas a 3 sobre el objeto GridLayout. Luego se fija el
    // controlador de posicionamiento para que sea el nuevo que
    // acabamos de modificar, y posteriormente se valida el Frame 
    // para asegurarse de que el alyout es valido y tendra efecto
    // sobre la visualizacion en pantalla
    public void actionPerformed( ActionEvent evt ) {
        miObjGridLayout.setRows( 2 );
        miObjGridLayout.setColumns( 3 );
        miObjFrame.setLayout( miObjGridLayout );
        miObjFrame.validate();
        }
    }

class Conclusion extends WindowAdapter {
    public void windowClosing( WindowEvent evt ) {
        // Termina la ejecucion del programa cuando se cierra la
        // ventana principal de la aplicacion
        System.exit( 0 );
        }
    }

Si se compila y ejecuta la aplicación, inicialmente aparecerá en pantalla una ventana semejante a la que se reproduce en la imagen siguiente:

A continuación se comentan algunas de las sentencias de código más interesantes del programa. Para comenzar, se instancian dos objetos Button que posteriormente serán los botones funcionales del programa. Estos objetos no pueden ser instanciados anónimamente, precisamente porque sobre ellos se va a registrar un objeto de tipo ActionListener, y es necesario que pueda tener una variable de referencia sobre cada objeto para permitir ese registro.

Button boton7 = new Button( "3x2" );
Button boton8 = new Button( "2x3" );

La siguiente sentencia instancia un objeto GridLayout que será utilizado como controlador de posicionamiento para uno de los objetos Panel. Tampoco este objeto puede ser anónimo, porque se necesitará acceso a él a la hora de modificar la colocación de los Componentes sobre ese Panel.

GridLayout miGridLayout = new GridLayout( 2,3 );

En el siguiente trozo de código se instancia uno de los objetos Panel que se integrarán en el objeto Frame principal. Se usará el objeto GridLayout, instanciado antes, como controlador de posicionamiento en el panel. Y, a continuación, se usa un bucle para colocar los seis objetos Button en el Panel. En este caso, sí es posible hacer que los objetos Button sean anónimos, ya que no se va a registrar sobre ellos ningún tipo de receptor de eventos.

Panel panel1 = new Panel();
// Fijamos el layout que habiamos definido para el panel
panel1.setLayout( miGridLayout );
// Se colocan los seis botones sobre el panel con una
// etiqueta indicando su numero
for( int i=0; i < 6; i++)
    panel1.add( new Button( "Boton"+i ) );

Ahora se instancia el segundo objeto Panel, y se colocan en él los dos botones que se habían instanciado antes, que son los que van a tener funcionalidad. Esto se hace en las siguientes sentencias:

Panel panel2 = new Panel();
panel2.add( boton7 );
panel2.add( boton8 );

El próximo paso es instanciar un objeto Frame que servirá como interfaz. Una vez que se instancie, se colocarán en él los dos objetos Panel utilizando su controlador de posicionamiento por defecto, el BorderLayout.

Frame miFrame = new Frame( "Tutorial de Java, AWT" );
// IMPORTANTE: Se añaden los dos objetos Panel que se han
// preparado al objeto Frame para crear el interfaz definitivo
miFrame.add( panel1,"North" );
miFrame.add( panel2,"South" );

En este instante, la creación física del interfaz está concluida, así que el siguiente paso es instanciar y registrar un objeto anónimo receptor de eventos de tipo Action, para procesar los eventos de los dos objetos Button que van a permitir cambiar la apariencia de los botones.

boton7.addActionListener( new A3x2ActionListener( miGridLayout,miFrame ) );
boton8.addActionListener( new A2x3ActionListener( miGridLayout,miFrame ) );

El código del controlador de eventos, tal como se puede ver en las sentencias que se reproducen a continuación, hace uso de los métodos de la clase del Layout para ejecutar las acciones pertinentes, a la recepción de un evento. En este caso, un evento provocado por uno de los objetos Button hace que los Componentes se coloquen en una malla de 3 filas por 2 columnas; y un evento sobre el otro objeto Button hace que se recoloquen en dos filas y tres columnas. El código del otro controlador es semejante.

public void actionPerformed( ActionEvent evt ) {
    miObjGridLayout.setRows( 3 );
    miObjGridLayout.setColumns( 2 );
    miObjFrame.setLayout( miObjGridLayout );
    miObjFrame.validate();
    }

GridBagLayout

Es igual que la composición de GridLayout, con la diferencia que los Componentes no necesitan tener el mismo tamaño. Es quizá el controlador de posicionamiento más sofisticado de los que actualmente soporta AWT.

A la hora de ponerse a trabajar con este controlador de posicionamiento, hay que tomar el rol de un auténtico aventurero. Parece que la filosofía de la gente de JavaSoft es que todo debe hacerse en el código. La verdad es que hasta que no haya en Java algo semejante a los recursos de X, el trabajo del programador, si quiere prescindir de herramientas de diseño, es un tanto prehistórico en su forma de hacer las cosas.

Si el lector acepta una recomendación, el consejo es que evite como la peste el uso del GridBagLayout, porque tanta sofisticación lo único que acarrea son dolores de cabeza; y, siempre se puede recurrir a la técnica de combinar varios paneles utilizando otros controladores de posicionamiento, dentro del mismo programa. Loa applets no apreciarán esta diferencia, al menos no tanto como para justificar los problemas que conlleva el uso del GridBagLayout.

A pesar de los pesares, se ha implementado como muetra el ejemplo java1326.java, un pograma que presenta diez botones en pantalla, con la apariencia que muestra la figura anterior.

import java.awt.*;

public class java1326 {
    public static void main( String args[] ) {
        IHM ihm = new IHM();
        }
    }

class IHM {
    public IHM() {
        Frame miFrame = new Frame( "Tutorial de Java, AWT" );
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints gbc = new GridBagConstraints();

        miFrame.setLayout( gridbag );

        // Para este grupo fijamos la anchura y vamos variando alguna
        // de las caracteristicas en los siguientes, de tal forma que
        // los botones que aparecen en cada una de las lineas tengan
        // apariencia diferente en pantalla     
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 1.0;
        Button boton0 = new Button( "Botón 0" );
        gridbag.setConstraints( boton0,gbc );
        miFrame.add( boton0 );
        Button boton1 = new Button( "Botón 1" );
        gridbag.setConstraints( boton1,gbc );
        miFrame.add( boton1 );
        Button boton2 = new Button( "Botón 2" );
        gridbag.setConstraints( boton2,gbc );
        miFrame.add( boton2 );

        gbc.gridwidth = GridBagConstraints.REMAINDER;
        Button boton3 = new Button( "Botón 3" );
        gridbag.setConstraints( boton3,gbc );
        miFrame.add( boton3 );

        gbc.weightx = 0.0;
        Button boton4 = new Button( "Botón 4" );
        gridbag.setConstraints( boton4,gbc );
        miFrame.add( boton4 );

        gbc.gridwidth = GridBagConstraints.RELATIVE;
        Button boton5 = new Button( "Botón 5" );
        gridbag.setConstraints( boton5,gbc );
        miFrame.add( boton5 );

        gbc.gridwidth = GridBagConstraints.REMAINDER;
        Button boton6 = new Button( "Botón 6" );
        gridbag.setConstraints( boton6,gbc );
        miFrame.add( boton6 );

        gbc.gridwidth = 1;
        gbc.gridheight = 2;
        gbc.weighty = 1.0;
        Button boton7 = new Button( "Botón 7" );
        gridbag.setConstraints( boton7,gbc );
        miFrame.add( boton7 );

        gbc.weighty = 0.0;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.gridheight = 1;
        Button boton8 = new Button( "Botón 8" );
        gridbag.setConstraints( boton8,gbc );
        miFrame.add( boton8 );
        Button boton9 = new Button( "Botón 9" );
        gridbag.setConstraints( boton9,gbc );
        miFrame.add( boton9 );
        miFrame.setSize( 250,150 );
        miFrame.setVisible( true );
        }
    }

Para aprovechar de verdad todas las posibilidades que ofrece este layout, hay que pintar antes en papel como van a estar posicionados los Componentes; utilizar gridx , gridy, gridwidth y gridheight en vez de GridBagConstraints.RELATIVE, porque en el proceso de validación del layout pueden quedar todos los Componentes en posición indeseable. Además, se deberían crear métodos de conveniencia para hacer más fácil el posicionamiento de los Componentes. En resumen, que las complicaciones que genera el uso del GridBagLayout no compensan.

Pero hay una luz en el horizonte, porque Swing incorpora un controlador de posicionamiento nuevo que utiliza la técnica que se ha popularizado con Smalltalk "Springs and Struts", lo cual redudirá significativamente la necesidad de tener que recurrir al GridBagLayout.

No obstante, y por no dejar cojo en algún sentido al Tutorial, y también por si el lector, a pesar de las advertencias, quiere intentar la aventura de implementar su aplicación basándose en este controlador de posicionamiento, a continuación se tratan una serie de cuestiones para intentar explicar los parámetros y características que se pueden configurar dentro del GridBagLayout, y ya que sea el propio lector el que intente probar suerte con él.

Este controlador de posicionamiento coloca los Componentes en filas y columnas, pero permite especificar una serie de parámetros adicionales para ajustar mejor la posición y tamaño de los Componentes dentro de las celdas. Y, al contrario que ocurría con el GridLayout, las filas y columnas no tienen porque tener un tamaño uniforme.

El controlador utiliza en número de Componentes en la fila más larga, el número de filas totales y el tamaño de los Componentes, para determinar el número y tamaño de las celdas que va a colocar en la tabla. La forma de visualizarse este conjunto de celdas, puede determinarse a través de una serie de características recogidas en un objeto de tipo GridBagConstraints. Estas características, o propiedades, son las que se van a describir a continuación. El objeto GridBagConstraints se inicializa a unos valores de defecto, cada uno de los cuales puede ser ajustado para alterar la forma en que se presenta el Componentes dentro del layout.

El ejemplo java1332.java, es el que se va a utilizar para comprobar el efecto que causan las modificaciones en los parámetros del objeto GridBagConstraints. Más concretamente, se van a modificar las características del objeto a la hora de manipula el posicionamiento de los dos botones de la parte inferior del panel.

import java.awt.*;
import java.awt.event.*;

public class java1332 extends Frame {
    Panel panel;

    public java1332() {
        // Estos son los botones que se van a marear
        Button botAceptar,botCancelar;

        // Este es el panel que contiene a todos los componentes
        panel = new Panel();
        panel.setBackground( Color.white );
        add( panel );

        // Se crean los objetos del GridBag y se le asigna este
        // layout al panel
        GridBagConstraints gbc = new GridBagConstraints();
        GridBagLayout gridbag = new GridBagLayout();
        panel.setLayout( gridbag );

        // Se indica que los componentes pueden rellenar la zona 
        // visible en cualquiera de las dos direcciones, vertical
        // u horizontal
        gbc.fill = GridBagConstraints.BOTH;
        // Se redimensionan las columnas y se mantiene su relación
        // de aspecto isgual en todo el proceso
        gbc.weightx = 1.0;
 
        // Se crea la etiqueta que va a servir de título al
        // panel
        Label labTitulo = new Label( "GridBag Layout" );
        labTitulo.setAlignment( Label.CENTER );
        // Se hace que el componente Label sea el único que se
        // sitúe en la línea que lo contiene
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        // Se pasan al layout tanto el componente Label, como
        // el objeto GridBagConstraints que modifica su
        // posicionamiento
        gridbag.setConstraints( labTitulo,gbc );
        // Finalmente se añade la etiqueta al panel. El objeto
        // GridBagConstraints de este contenedor pone la etiqueta
        // en una línea, la redimensiona para que ocupe toda la
        // fila de la tabla, tanto horizontal como verticalmente,
        // y hace que las columnas se redimensionen de la misma
        // forma cuando la ventana cambie de tamaño
        panel.add( labTitulo );

        // Ahora se crea uno de los campos de texto, en este
        // caso para recoger un supuesto nombre
        TextField txtNombre = new TextField( "Nombre:",25 );
        // Se hace que el campo de texto sea el siguiente objeto
        // después del último que haya en la fila. Esto significa 
        // que todavía se puede añadir uno o más componentes a
        // la fila, a continuación del campo de texto
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        // Se pasan al layout tanto el campo de texto, como
        // el objeto GridBagConstraints que modifica su
        // posicionamiento
        gridbag.setConstraints( txtNombre,gbc );
        // Se añade el campo de texto al panel
        panel.add( txtNombre );
 
        // Se crea otro campo de texto, en este caso para recoger
        // la direccion del propietario del nombre anterior
        TextField txtDireccion = new TextField( "Direccion:",25 );
        // Se hace que este campo de texto sea el último
        // componente que se sitúe en la fila en que se 
        // encuentre
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        // Se pasan al layout tanto el campo de texto, como
        // el objeto GridBagConstraints que modifica su
        // posicionamiento
        gridbag.setConstraints( txtDireccion,gbc );
        // Se añade el campo de texto al panel
        panel.add( txtDireccion );
 
        // Se crea un área de texto para introducir las cosas
        // que quiera el que esté utilizando el programa
        TextArea txtComent = new TextArea( 3,25 );
        txtComent.setEditable( true );
        txtComent.setText( "Comentarios:" );
        // Se pasan al layout tanto el área de texto, como
        // el objeto GridBagConstraints que modifica su
        // posicionamiento
        gridbag.setConstraints( txtComent,gbc );
        // Se añade el área de texto al panel
        panel.add( txtComent );
 
        // Estos son los dos botones de la parte inferior del
        // panel, sobre los que vamos a modificar las
        // propiedades del objeto GridBagConstraints que 
        // controla su posicionamiento dentro del panel, para
        // ir mostrando el comportamiento del conjunto ante
        // esos cambios
        botAceptar = new Button( "Aceptar" );
        botCancelar = new Button( "Cancelar" );
  
        // Hacemos que el botón "Aceptar" no sea el último
        // de la fila y que no pueda expandirse en ninguna
        // dirección
        gbc.fill = GridBagConstraints.NONE;
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        // Se pasan al layout el botón y el objeto 
        // GridBagConstraints
        gridbag.setConstraints( botAceptar,gbc );
        // Se añade el botón al panel
        panel.add( botAceptar );

        // Se hace que el botón "Cancelar" sea el último de
        // la fila en que se encuentre
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        // Se hace que su altura no se reescale
        gbc.gridheight = 1;
        // Se pasan al layout el botón y el objeto 
        // GridBagConstraints
        gridbag.setConstraints( botCancelar,gbc );
        // Se añade el botón al panel
        panel.add( botCancelar );
  
        // Se añade el receptor de eventos de la ventana
        // para acabar la ejecución
        addWindowListener( new WindowAdapter() {
            public void windowClosing( WindowEvent evt ) {
                System.exit( 0 );
                }
            } );
        }

    public static void main( String args[] ) {
        java1332 miFrame = new java1332();
        
        // Fijamos el título de la ventana y la hacemos 
        // visible, con los componentes en su interior
        miFrame.setTitle( "Tutorial de Java, AWT" );
        miFrame.pack();
        miFrame.setVisible( true );
        }
    }

Tanto el código del ejemplo como los comentarios que se han insertado, deberían proporcionar al lector una idea de lo que hace cada una de las características del objeto GridBagConstraints y de cómo se usa. A continuación se describen estos parámetros, modificándolos sobre el ejemplo anterior.

gridx y gridy

Las propiedades gridx y gridy indican la fila y la columna, respectivamente, en donde se va a colocar el Componente. La primera fila de la parte superior es gridx=0, y la columna más a la izquierda corresponde a gridy=0.

Los valores para estos parámetros deben ser un entero correspondiendo a una casilla fila/columna explícita o el valor GridBagConstraints.RELATIVE, que indica que el Componente debe ser colocado en una fila/columna relativa a la fila/columna donde se ha colocado el último Componente.

Por ejemplo, cuando se añaden componentes a una fila, gridx=GridBagConstraints.RELATIVE, coloca el Componente a la derecha del último Componente que se haya colocado en la fila. Si un Componente se ha insertado en la fila 2 y columna 1, el siguiente Componente se insertará en la fila 2 y columna 2.

Cuando se añaden componente a una columna, gridy=GridBagConstraints.RELATIVE coloca el Componente por debajo del último Componente que se haya añadido a la columna. Si un Componente se ha insertado en la columna 0 y fila 1, el siguiente Componente se insertará en la columna 0 y fila 2.

Si se incorpora el siguiente trozo de código al ejemplo java1332.java, para fijar nuevos valores de gridx y gridy:

gbc.gridx = 1;
gbc.gridy = 3
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gridbag.setConstraints( botAceptar,gbc );
// Se añade el botón al panel
panel.add( botAceptar );
 
gbc.gridx = 0;
gbc.gridy = 3
gridbag.setConstraints( botCancelar,gbc );
// Se añade el botón al panel
panel.add( botCancelar );

se observa que el resultado de la ejecución ahora es el mismo, pero los botones Aceptar y Cancelar están cambiados de sitio, tal como reproduce la figura siguiente:

Se puede fijar el valor de gridx y gridy para evitar la colocación secuencial de componentes que realiza el sistema por defecto. En este contexto, ni GridBagConstraints.NONE ni tampoco GridBagConstraints.REMAINDER tienen significado alguno.

gridwidth y gridheight

Los parámetros o propiedades gridwidth y gridheight indican el número de celdas en la zona de presentación que va a ocupar un determinado Componente. Los valores por defecto son una fila de ancho y una columna de alto, es decir, gridwidth=1 y gridheight=1.

Se puede conseguir que un Componente ocupe dos columnas de ancho indicando gridwith=2 o tres filas de alto con gridheight=3. Si se fija gridwidth a GridBagConstraints.REMAINDER, se indica que el Componente debería ocupar el número de celdas que queden libres en la fila. Fijándolo a GridBagConstraints.RELATIVE se indica que el Componente debería ocupar todas las celdas que queden en la fila, excepto la última.

Cuando se indica que un Componente es el último o el siguiente al último Componente de la fila, se puede proporcionar un valor de gridheight más grande que la unidad y el Componente se expandirá por varias filas. Sin embargo, en este caso, no hay forma de hacer que el Componente sea más ancho que una columna, cuando sea el último o el siguiente al último de la fila.

Si se añade el siguiente trozo de código al ejemplo principal java1332.java:

gbc.weigthy = 1.0;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.gridheight = 2;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gridbag.setConstraints( botAceptar,gbc );
// Se añade el botón al panel
panel.add( botAceptar );
 
gbc.fill = GridBagConstraints.NONE;
gbc.gridheight = 1;
gridbag.setConstraints( botCancelar,gbc );
// Se añade el botón al panel
panel.add( botCancelar );

El resultado que se obtiene es el que se muestra en la figura que aparece a continuación, que es la imagen capturada de la ventana:

En ella se puede observar que el botón Aceptar es dos veces más alto que el botón Cancelar, tal como se ha fijado en el código.

weightx y weighty

Cuando el usuario redimensiona una ventana que utiliza el GridBagLayout como controlador de posicionamiento de los componentes, los parámetros weightx y weighty determinan la forma en que se van a redimensionar. Por defecto, los valores de estos parámetros son weightx=0 y weighty=0, lo que significa que cuando la ventana es redimensionada, los componentes permanecen juntos y agrupados en el centro del contenedor.

Si se proporciona un valor mayor que 0 para weightx, los componentes se expandirán en la dirección x, horizontalmente. Si se proporciona un valor mayor que 0 para weighty, los componentes se expandirán en la dirección y, verticalmente.

En las dos modificaciones anteriores que se han hecho al ejemplo java1332.java, se puede comprobar el efecto de estos parámetros. En la primera modificación, donde se alteraban los valores de gridx y gridy, en el código, c.weigthx=1.0 y c.weigthy=0.0, permiten que los componentes se redimensionen junto con la ventana, horizontalmente, pero permanecen agrupados en el centro, verticalmente.

En la segunda modificación, correspondiente a gridwidth y gridheight, el valor de c.weighty se ha cambiado de 0.0 a 1.0, utilizando un valor para gridheight de 2 y un valor de fill fijado a GridBagConstraints.VERTICAL; esto hace que el botón aceptar ocupe dos filas, pero se expande solamente en dirección vertical.

fill

El parámetro fill determina la forma en que un Componente rellena el área definida por gridx/gridy/gridwidth/gridheight; y los valores que puede tomar este parámetro pueden ser:

GridBagConstraints.HORIZONTAL: El Componente se expande horizontalmente para rellenar todo el área de visualización.
GridBagConstraints.VERTICAL: El Componente se expande verticalmente para rellenar todo el área de visualización.
GridBagConstraints.BOTH: El Componente se expande completamente para ocupar la totalidad del área de visualización.
GridBagConstraints.NONE: El Componente es reducido a su tamaño ideal, independientemente del tamaño que tenga la zona de visualización.

En la modificación que se hizo del código del ejemplo java1332.java para mostrar el efecto de la modificación de los parámetros gridwidth y gridheight, el valor de c.fill se cambia de GridBagConstraints.BOTH a GridBagConstraints.VERTICAL y se utiliza con un valor de weighty fijado a 1 y un valor de 2 para gridheight, así que el botón Aceptar se expande a dos filas, pero siempre llena completamente la zona verticalmente.

Si ahora se añade el siguiente trozo de código al ejemplo java1332.java, se provocará el relleno completo de la zona de visualización en dirección horizontal.

gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gridbag.setConstraints( botAceptar,gbc );
// Se añade el botón al panel
panel.add( botAceptar );
 
gbc.gridheight = 1;
gridbag.setConstraints( botCancelar,gbc );
// Se añade el botón al panel
panel.add( botCancelar );

Como se puede observar en la figura siguiente, en la que se reproduce el efecto causado por el código anterior, los botones también ocupan completamente la fila, porque hay dos botones y dos columnas. El área de texto también ocupa la fila completamente porque el parámetro fill está fijado a GridBagConstraints.BOTH y no hay ningún otro Componente en la segunda columna de la fila.

anchor

Cuando un Componente es más pequeño que la zona de visualización, se puede colocar en una determinada posición utilizando el parámetro anchor que puede tomar el valor GridBagConstrints.CENTER, que es el que toma por defecto, o cualquiera de las direcciones de los puntos cardinales: NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST.

Si se añade el siguiente trozo de código al ejemplo java1332.java, que ya debe estar mareado del todo, el botón Aceptar se desplazará hacia la parte oeste de la fila, mientras que el botón Cancelar sigue en el centro de su celda.

gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gridbag.setConstraints( botAceptar,gbc );
// Se añade el botón al panel
panel.add( botAceptar );

gbc.anchor = GridBagConstraints.CENTER;
gbc.gridheight = 1;
gridbag.setConstraints( botCancelar,gbc );
// Se añade el botón al panel
panel.add( botCancelar );

La imagen siguiente reproduce la situación generada por la modificación anterior, en la que se pueden observar las posiciones de los botones tal como se refiere.

A pesar de ajustar este parámetro, hay que asegurarse de haber fijado el valor de fill adecuadamente, porque si el Componente rellena la zona de visualización en la dirección equivocada, en anclaje del Componente puede quedar sin efecto. Por ejemplo, si se tiene un Componente que se expande por dos filas y tiene un fill vertical, el anclaje del Componente en el Norte, Centro o Sur no alterará en nada su apariencia. Si el fill es solamente vertical y no está fijado el fill horizontal, el anclaje en cualquiera otra de las posiciones, que no sean las tres anteriores, sí que cambiará la posición del Componente sobre el área de visualización.

ipadx e ipady

El controlador de posicionamiento calcula el tamaño de un Componente basándose en los parámetros del GridBagLayout y en los otros componentes que se encuentren sobre el layout. Se puede indicar un desplazamiento interno para incrementar el tamaño calculado para un Componente y hacer que ese Componente sea más ancho, ipadx, o más alto, ipady, que el tamaño real que calcularía el controlador, aunque no llegue a llenar completamente la zona de visualización en dirección horizontal o vertical, al estilo que hace el parámetro fill.

Los valores de defecto de ipadx e ipady son 0. Cuando se especifica un valor para ipadx, el ancho total del Componente se calcula sumando a su ancho real, dos veces la cantidad que se haya indicado en ipadx, ya que el desplazamiento se añade por ambos lados del Componente. Cuando se especifica un valor para ipady, la altura total del Componente se calcula sumando a su altura real, dos veces la cantidad que se haya indicado en ipady, ya que el desplazamiento se añade por arriba y por abajo del Componente.

Si se incorpora al ejemplo java1332.java, el trozo de código que se reproduce a continuación, se obtendrá como resultado una imagen semejante a las anteriores, pero los botones serán 25 píxels más anchos por cada lado.

gbc.fill = GridBagConstraints.NONE;
gbc.ipadx = 50;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gridbag.setConstraints( botAceptar,gbc );
// Se añade el botón al panel
panel.add( botAceptar );
 
gbc.gridheight = 1;
gridbag.setConstraints( botCancelar,gbc );
// Se añade el botón al panel
panel.add( botCancelar );

La imagen siguiente muestra el efecto del código anterior, observándose que efectivamente los botones son más anchos que los de los ejemplos anteriores, y no llegan a ocupar toda la fila como en el caso de fill.

insets

El parámetro insets permite especificar la mínima cantidad de espacio que debe haber entre el Componente y los bordes del área de visualización. Por defecto, el espacio determinado por insets se deja en blanco, pero también se puede rellenar con un color, o una imagen o un título. La imagen siguiente muestra el gráfico explicativo, que seguro es más comprensible que lo escrito.

Como valor de este parámetro hay que pasarle un objeto de tipo Insets, al cual se le especifica el espacio que se quiere dejar en cada uno de los cuatro lados del Componente.

Si se añade el siguiente trozo del código al ejemplo java1332.java, se ampliará en 15 pixels el espacio que hay entre la fila en que se encuentran los botones Aceptar y Cancelar y el área de texto que está por encima.

gbc.insets = new Insets( 15,0,0,0 );
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gridbag.setConstraints( botAceptar,gbc );
// Se añade el botón al panel
panel.add( botAceptar );
 
gbc.gridheight = 1;
gridbag.setConstraints( botCancelar,gbc );
// Se añade el botón al panel
panel.add( botCancelar );

La imagen siguiente reproduce el efecto generado por la modificación anterior, en donde se puede observar el desplazamiento comentado.

Como se puede observar, el GridBagLayout es el más poderoso y flexible de los controladores de posicionamiento que proporciona el AWT. Sin embargo, el llegar a dominar los parámetros que lo pueden modificar no es nada sencillo y lo más habitual es que se generen efectos visuales indeseados a la hora de redimensionar la ventana, incluso prestando suficiente atención en la asignación de los valores de los parámetros. Por ello, el autor recomienda al lector que si puede utilizar cualquier otro controlador de posicionamiento, o incluso una combinación de ellos, que evite el uso de GridBagLayout, porque su dominio no es sencillo y, además, nunca se está seguro de cómo se va a comportar.

Navegador

Home | Anterior | Siguiente | Indice | Correo