Tutorial de Java

AWT - Layouts (I)

Anterior | Siguiente

Los layout managers o manejadores de composición, en traducción literal, ayudan a adaptar los diversos Componentes que se desean incorporar a un Panel, es decir, especifican la apariencia que tendrán los Componentes a la hora de colocarlos sobre un Contenedor, controlando tamaño y posición (layout) automáticamente. Java dispone de varios, en la actual versión, tal como se muestra en la imagen:

¿Por qué Java proporciona estos esquemas predefinidos de disposición de componentes? La razón es simple: imaginemos que se desean agrupar objetos de distinto tamaño en celdas de una rejilla virtual: si confiados en nuestro conocimiento de un sistema gráfico determinado, se codificase a mano tal disposición, se debería preveer el redimensionamiento del applet, su repintado cuando sea cubierto por otra ventana, etc., además de todas las cuestiones relacionadas con un posible cambio de plataforma (uno nunca sabe a donde van a ir a parar los propios hijos, o los applets).

Sigamos imaginando, ahora, que un hábil equipo de desarrollo ha previsto las disposiciones gráficas más usadas y ha creado un gestor para cada una de tales configuraciones, que se ocupará, de forma transparente para nosotros, de todas esas cuitas de formatos. Bien, pues estos gestores son instancias de las distintas clases derivadas de LayoutManager y que se utilizan en el applet que genera la figura siguiente, donde se muestran los diferentes tipos de layouts que proporciona el AWT.

El ejemplo java1320.java, ilustra el uso de paneles, listas, barras de desplazamiento, botones, selectores, campos de texto, áreas de texto y varios tipos de layouts.

En el tratamiento de los Layouts se utiliza un método de validación, de forma que los Componentes son marcados como no válidos cuando un cambio de estado afecta a la geometría o cuando el Contenedor tiene un hijo incorporado o eliminado. La validación se realiza automáticamente cuando se llama a pack() o show(). Los Componentes visibles marcados como no válidos no se validan automáticamente.

FlowLayout

Es el más simple y el que se utiliza por defecto en todos los Paneles si no se fuerza el uso de alguno de los otros. Los Componentes añadidos a un Panel con FlowLayout se encadenan en forma de lista. La cadena es horizontal, de izquierda a derecha, y se puede seleccionar el espaciado entre cada Componente.

Si el Contenedor se cambia de tamaño en tiempo de ejecución, las posiciones de los Componentes se ajustarán automáticamente, para colocar el máximo número posible de Componentes en la primera línea.

Los Componentes se alinean según se indique en el constructor. Si no se indica nada, se considera que los Componentes que pueden estar en una misma línea estarán centrados, pero también se puede indicar que se alineen a izquierda o derecha en el Contenedor.

El ejemplo que se presenta a continuación, java1321.java, es muy sencillito y lo que hace es colocar cinco objetos Button, sin funcionalidad alguna, sobre un objeto Frame, utilizando como controlador de posicionamiento un FlowManager. Los botones no son funcionales porque no se registra ningún objeto receptor de eventos sobre ellos.

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

public class java1321 {
    public static void main( String args[] ) {
        // Instancia un objeto de tipo Interfaz Hombre-Maquina
        IHM ihm = new IHM();
        }
    }

// La siguiente clase se utiliza para instanciar un objeto de tipo
// Interfaz Grafica de Usuario
class IHM {
    public IHM() {
        // Se crea un objeto Button con el texto que se pasa como
        // parametro y el tamaño y posicion indicadas dentro de
        // su contenedor (en pixels)
        Button miBoton = new Button( "Boton" );
        // Al rectamgulo se le pasan los parametros: x,y,ancho,alto
        miBoton.setBounds( new Rectangle( 25,20,100,75 ) );

        // Se crea un objeto Label con el texto que se indique como
        // parametro en la llamada y el tamaño especificado y en la
        // posicion que se indique dentro de su contenedor (en pixels)
        // Se pone en amarillo para que destaque
        Label miEtiqueta = new Label( "Tutorial de Java" );
        miEtiqueta.setBounds( new Rectangle( 100,75,100,75 ) );
        miEtiqueta.setBackground( Color.yellow );

        // Se crea un objeto Frame con el titulo que se indica en la
        // lamada y sin ningun layout
        Frame miFrame = new Frame( "Tutorial de Java, AWT" );
        miFrame.setLayout( null );

        // Añade los dos componentes al Frame, fijando su tamaño en
        // pixels y lo hace visible
        miFrame.add( miBoton );
        miFrame.add( miEtiqueta );
        miFrame.setSize( 250,175 );
        miFrame.setVisible( true );
        }
    }

Al compilar y ejecutar el programa, en pantalla aparecerá inicialmente una ventana como la que se muestra en la siguiente imagen.

Como se puede observar en el código del ejemplo, el objeto FlowLayout se construye con alineación izquierda, una separación horizontal entre Componentes de 10 pixels y una separación vertical de 15 pixels. Si se cambia de tamaño manualmente al Frame, ya con el programa en ejecución, las posiciones de los Componentes se ajustan automáticamente, colocando el número máximo posible de ellos en la primera línea.

Ahora se presenta otro ejemplo, java1322.java, que aunque tampoco haga nada espectacular, por lo menos permite ver cómo se modifica un layout dinámicamente en tiempo de ejecución. En el programa se añaden cinco botones a un Frame utilizando un objeto FlowLayout como manejador de posicionamiento de estos botones, fijando una separación de 3 pixels entre los Componentes, tanto en dirección horizontal como vertical.

Se instancia y registra un objeto receptor de eventos de tipo acción para recoger los eventos de los cinco botones. La acción del controlador de eventos es incrementar el espacio entre los Componentes en 5 pixels al pulsar cualquiera de los botones. Esto se consigue incrementando los atributos Vgap y Hgap del objeto FlowLayout, fijando como controlador de posicionamiento el layout modificado y validando el Frame. Este último paso es imprescindible para que los cambios tengan efecto y se hagan visibles.

También se instancia y registra un objeto receptor de eventos windowClosing() para terminar el programa cuando se cierre el Frame. El código del ejemplo es el siguiente.

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

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

class IHM {
    public IHM() {
        Frame miFrame = new Frame( "Tutorial de Java, AWT" );
        // Instancia un objeto FlowLayout object alieado al Centro
        // y con una separacion de 3 pixels en horizonal y vertical
        FlowLayout miFlowLayout = new FlowLayout( FlowLayout.CENTER,3,3 );

        // Se fija este FlowLayout para que sea el controlador de
        // posicionamiento de componentes para el objeto Frame
        miFrame.setLayout( miFlowLayout );

        // Se instancian cinco objetos Button, para indicar los
        // posicionamientos del FlowLayout 
        Button boton1 = new Button( "Primero" );
        Button boton2 = new Button( "Segundo" );
        Button boton3 = new Button( "Tercero" );
        Button boton4 = new Button( "Cuarto" );
        Button boton5 = new Button( "Quinto" );

        // Se añaden los cinco botones al Frame en las mismas posiciones
        // que vienen dadas por las etiquetas que se les han asignado en
        // el constructor
        miFrame.add( boton1 );
        miFrame.add( boton2 );
        miFrame.add( boton3 );
        miFrame.add( boton4 );
        miFrame.add( boton5 );

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

        // Instancia un objeto receptor de eventos de tipo action y
        // lo registra para los cinco botones que se han añadido al
        // objeto Frame
        MiReceptorAction miReceptorAction = 
            new MiReceptorAction( miFlowLayout,miFrame );
        boton1.addActionListener( miReceptorAction );
        boton2.addActionListener( miReceptorAction );
        boton3.addActionListener( miReceptorAction );
        boton4.addActionListener( miReceptorAction );
        boton5.addActionListener( miReceptorAction );

        // Se instancia y registra un receptor de eventos de ventana
        // para terminar la ejecucion del programa cuando se cierre
        // el Frame  
        miFrame.addWindowListener( new Conclusion() );
        }
    }

class MiReceptorAction implements ActionListener {
    FlowLayout miObjLayout;
    Frame miObjFrame;

    MiReceptorAction( FlowLayout layout,Frame frame ) {
        miObjLayout = layout;
        miObjFrame = frame;
        }

    // Cuando sucede un evento Action, se incrementa el espacio que
    // que hay entre los componentes en el objeto FlowLayout.
    // Luego se fija el controlador de posicionamiento al nuevo
    // que se construye, y luego se valida el Frame para asegurar
    // que se actualiza en la pantalla
    public void actionPerformed( ActionEvent evt ){
        miObjLayout.setHgap( miObjLayout.getHgap()+5 );
        miObjLayout.setVgap( miObjLayout.getVgap()+5 );    
        miObjFrame.setLayout( miObjLayout );
        miObjFrame.validate();
        }
    }

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

El programa contiene algunas sentencias que merecen un comentario. Por ejemplo, el código que se reproduce a continuación instancia un objeto FlowLayout con alineación central y con 3 pixels de separación entre Componentes, tanto vertical como horizontalmente. Este objeto se pasa al método setLayout() para que sea el controlador de posicionamiento para el objeto Frame.

// Instancia un objeto FlowLayout object alieado al Centro
// y con una separacion de 3 pixels en horizonal y vertical
FlowLayout miFlowLayout = new FlowLayout( FlowLayout.CENTER,3,3 );

// Se fija este FlowLayout para que sea el controlador de
// posicionamiento de componentes para el objeto Frame
miFrame.setLayout( miFlowLayout );

La siguientes sentencias se utilizan para instanciar un objeto receptor de eventos Action, un ActionListener, y lo registran sobre los cinco botones:

MiReceptorAction miReceptorAction = 
    new MiReceptorAction( miFlowLayout,miFrame );
boton1.addActionListener( miReceptorAction );

Y el código que sigue, ya es el programa controlador de eventos, que modifica el layout dinámicamente en tiempo de ejecución. Este código responde, cuando se pulsa cualquiera de los botones, utilizando los métodos get() y set() sobre los atributos de separación vertical, Vgap, y horizontal, Hgap, del FlowLayout; modificando el espaciado entre Componentes. Luego, se utiliza el método setLayout() para hacer que el objeto Layout así modificado sea el controlador de posicionamiento del objeto Frame, y, por fin, se hace que los cambios sean efectivos y se visualicen en pantalla validando el objeto Frame, a través de una llamada al método de validación, validate().

public void actionPerformed( ActionEvent evt ){
    miObjLayout.setHgap( miObjLayout.getHgap()+5 );
    miObjLayout.setVgap( miObjLayout.getVgap()+5 );    
    miObjFrame.setLayout( miObjLayout );
    miObjFrame.validate();
    }

BorderLayout

La composición BorderLayout (de borde) proporciona un esquema más complejo de colocación de los Componentes en un panel. La composición utiliza cinco zonas para colocar los Componentes sobre ellas: Norte, Sur, Este, Oeste y Centro. Es el layout o composición que se utilizan por defecto Frame y Dialog.

El Norte ocupa la parte superior del panel, el Este ocupa el lado derecho, Sur la zona inferior y Oeste el lado izquierdo. Centro representa el resto que queda, una vez que se hayan rellenado las otras cuatro partes. Así, este controlador de posicionamiento resuelve los problemas de cambio de plataforma de ejecución de la aplicación, pero limita el número de Componentes que pueden ser colocados en Contenedor a cinco; aunque, si se va a construir un interfaz gráfico complejo, algunos de estos cinco Componentes pueden Contenedores, con lo cual el número de Componentes puede verse ampliado.

En los cuatro lados, los Componentes se colocan y redimensionan de acuerdo a sus tamaños preferidos y a los valores de separación que se hayan fijado al Contenedor. El tamaño prefijado y el tamaño mínimo son dos informaciones muy importantes en este caso, ya que un botón puede ser redimensionado a proporciones cualesquiera; sin embargo, el diseñador puede fijar un tamaño preferido para la mejor apariencia del botón. El controlador de posicionamiento puede utilizar este tamaño cuando no haya indicaciones de separación en el Contenedor, o puede ignorarlo, dependiendo del esquema que utilice. Ahora bien, si se coloca una etiqueta en el botón, se puede indicar un tamaño mínimo de ese botón para que siempre sea visible, al menos, el rótulo del botón. En este caso, el controlador de posicionamiento muestra un total respeto a este valor y garantiza que por lo menos ese espacio estará disponible para el botón.

El ejemplo que se verá a continuación, java1323.java, es muy simple. Lo que hace es crear una ventana a través de un objeto Frame y colocar cinco objetos Button, sin funcionalidad alguna, utilizando un BorderLayout como manejador de composición. A uno de los botones se le ha colocado un texto más largo, para que el controlador de posicionamiento reserve espacio de acuerdo al tamaño mínimo que se indique para ese botón.

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

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

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

        miFrame.add( new Button( "Sur" ),"South" );
        miFrame.add( new Button( "Oeste" ),"West" );
        miFrame.add( new Button( "Este" ),"North" );
        miFrame.add( new Button( "Boton del Este" ),"East" );
        miFrame.add( new Button( "Centro" ),"Center" );
        miFrame.setSize( 250,150 );
        miFrame.setVisible( true );
        }
    }

Si se compila y ejecuta este programa, aparece en pantalla una imagen como la que se reproduce a continuación:

Se observará que se puede cambiar de tamaño el objeto Frame, y que dentro de los límites, los Componentes se van cambiado a la vez, para acomodarse al tamaño que va adoptando la ventana. Aunque todavía hay problemas que no se han eliminado, y se podrá redimensionar la ventana para conseguir que los botones desaparezcan, o se trunquen los rótulos. Sin embargo, es una forma muy flexible de posicionar Componentes en una ventana y no tener que preocuparse de su redimensionamiento y colocación dentro de unos límites normales y sin buscarle las cosquillas al sistema.

El siguiente ejemplo, java1324.java, aunque tampoco hace nada particularmente interesante, sí tiene más sustancia que el anterior e ilustra algunos aspectos adicionales del BorderLayout. En el programa se añaden cinco botones a un Frame utilizando un objeto BorderLayout como manejador de posicionamiento de estos botones, fijando una separación de 3 pixels entre los Componentes, tanto en dirección horizontal como vertical.

Se instancia y registra un objeto receptor de eventos de tipo acción para recoger los eventos de los cinco botones. La acción del controlador de eventos es incrementar el espacio entre los Componentes en 5 pixels al pulsar cualquiera de los botones. Esto se consigue incrementando los atributos Vgap y Hgap del objeto BorderLayout, fijando como controlador de posicionamiento el layout modificado y validando el Frame. Este último paso es imprescindible para que los cambios tengan efecto y se hagan visibles.

También se instancia y registra un objeto receptor de eventos windowClosing() para terminar el programa cuando se cierre el Frame.

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

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

class IHM {
    public IHM() {
        Frame miFrame = new Frame( "Tutorial de Java, AWT" );
        // Se instancia un objeto BorderLayout con una holgura en vertical y
        // horizontal de 3 pixels
        BorderLayout miBorderLayout = new BorderLayout( 3,3 );
        // Se fija este BorderLayout para que sea el controlador de
        // posicionamiento de componentes para el objeto Frame
        miFrame.setLayout( miBorderLayout );

        // Se instancian cinco objetos Button, para indicar los
        // posicionamientos del BorderLayout 
        Button boton1 = new Button( "Sur" );
        Button boton2 = new Button( "Oeste" );
        Button boton3 = new Button( "Norte" );
        Button boton4 = new Button( "Este" );
        Button boton5 = new Button( "Centro" );

        // Se añaden los cinco botones al Frame en las mismas posiciones
        // que vienen dadas por las etiquetas que se les han asignado en
        // el constructor
        miFrame.add( boton1,"South" );
        miFrame.add( boton2,"West" );
        miFrame.add( boton3,"North" );
        miFrame.add( boton4,"East" );
        miFrame.add( boton5,"Center" );

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

        // Instancia un objeto receptor de eventos de tipo action y
        // lo registra para los cinco botones que se han añadido al
        // objeto Frame
        MiReceptorAction miReceptorAction = 
            new MiReceptorAction( miBorderLayout,miFrame );
        boton1.addActionListener( miReceptorAction );
        boton2.addActionListener( miReceptorAction );
        boton3.addActionListener( miReceptorAction );
        boton4.addActionListener( miReceptorAction );
        boton5.addActionListener( miReceptorAction );

        // Se instancia y registra un receptor de eventos de ventana
        // para terminar la ejecucion del programa cuando se cierre
        // el Frame
        miFrame.addWindowListener( new Conclusion() );
        }
    }

class MiReceptorAction implements ActionListener{
    BorderLayout miObjBorderLayout;
    Frame miObjFrame;

    MiReceptorAction( BorderLayout layout,Frame frame ) {
        miObjBorderLayout = layout;
        miObjFrame = frame;
        }

    // Cuando sucede un evento Action, se incrementa el espacio que
    // que hay entre los componentes en el objeto BorderLayout.
    // Luego se fija el controlador de posicionamiento al nuevo
    // que se construye, y luego se valida el Frame para asegurar
    // que se actualiza en la pantalla
    public void actionPerformed( ActionEvent evt ) {
        miObjBorderLayout.setHgap( miObjBorderLayout.getHgap()+5 );
        miObjBorderLayout.setVgap( miObjBorderLayout.getVgap()+5 );    
        miObjFrame.setLayout( miObjBorderLayout );
        miObjFrame.validate();
        }
    }

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

Seguidamente se comentan algunas de las sentencias más interesantes que constituyen el código del programa. Por ejemplo, las líneas de código siguientes:

Frame miFrame = new Frame( "Tutorial de Java, AWT" );
// Se instancia un objeto BorderLayout con una holgura en vertical y
// horizontal de 3 pixels
BorderLayout miBorderLayout = new BorderLayout( 3,3 );
// Se fija este BorderLayout para que sea el controlador de
// posicionamiento de componentes para el objeto Frame
miFrame.setLayout( miBorderLayout );

instancian un objeto Frame, también instancian un objeto BorderLayout con una separación de tres pixels entres Componentes y establecen ese objeto BorderLayout como controlador de posicionamiento de Componentes del Frame.

Las sentencias

Button boton1 = new Button( "Sur" );
. . . 
miFrame.add( boton1,"South" );

son las típicas utilizadas para la instanciación de los objetos Button y para añadir estos objetos al objeto Frame.

Las sentencias siguientes son las que instancian un objeto receptor de eventos de tipo Action, y lo registran sobre los cinco botones.

MiReceptorAction miReceptorAction = 
    new MiReceptorAction( miBorderLayout,miFrame );
boton1.addActionListener( miReceptorAction );

El código que sigue ahora, ya en el controlador de eventos actionPerformed() utiliza los métodos getHgap(), getVgap(), setHgap() y setVgap() para modificar los atributos de separación entre Componentes en el BorderLayout. Este BorderLayout modificado es utilizado, en conjunción con setLayout(), para convertirlo en el controlador de posicionamiento del objeto Frame. Y, por último, se utiliza el método validate() para forzar al objeto Frame a reajustar el tamaño y posición de los Componentes y presentar en pantalla la versión modificada de sí mismo.

public void actionPerformed( ActionEvent evt ) {
    miObjBorderLayout.setHgap( miObjBorderLayout.getHgap()+5 );
    miObjBorderLayout.setVgap( miObjBorderLayout.getVgap()+5 );    
    miObjFrame.setLayout( miObjBorderLayout );
    miObjFrame.validate();
    }

Navegador

Home | Anterior | Siguiente | Indice | Correo