miércoles, 27 de octubre de 2010

LAYOUTS (ADMINISTRADORES DE ESQUEMAS)

LAYOUTS
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.
¿Por qué Java proporciona estos esquemas predefinidos de disposición de componentes? La razón es simple: imaginemos que deseamos agrupar objetos de distinto tamaño en celdas de una rejilla virtual: si confiados en nuestro conocimiento de un sistema gráfico determinado, codificamos a mano tal disposición, deberemos 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).
Con un layout adecuado, el método pack() de la ventana hará que coja el tamaño necesario para que se vea todo lo que tiene dentro.
ventana.pack();
Las ventanas vienen con un Layout por defecto. En java hay varios layouts disponbiles y podemos cambiar el de defecto por el que queramos.

El Layout null
Uno de los Layouts más utilizados por la gente que empieza, por ser el más sencillo, es NO usar layout. Somos nosotros desde código los que decimos cada botón en qué posición va y qué tamaño ocupa
contenedor.setLayout(null);  // Eliminamos el layout
contenedor.add (boton); // Añadimos el botón
boton.setBounds (10,10,40,20); // Botón en posicion 10,10 con ancho 40 pixels y alto 20

Esto, aunque sencillo, no es recomendable. Si estiramos la ventana los componentes seguirán en su sitio, no se estirarán con la ventana. Si cambiamos de sistema operativo, resolución de pantalla o fuente de letra, tenemos casi asegurado que no se vean bien las cosas: etiquetas cortadas, letras que no caben, etc.
Además, al no haber layout, la ventana no tiene tamaño adecuado. Deberemos dárselo nosotros con un ventana.setSize(...).
El tiempo que ahorramos no aprendiendo cómo funcionan los Layouts, lo perderemos echando cuentas con los pixels, para conseguir las cosas donde queremos, sólo para un tipo de letra y un tamaño fijo.

FlowLayout
El FlowLayout es bastante sencillo de usar. Nos coloca los componente en fila. Hace que todos quepan (si el tamaño de la ventana lo permite). Es adecuado para barras de herramientas, filas de botones, etc.
contenedor.setLayout(new FlowLayout());
contenedor.add(boton);
contenedor.add(textField);
contenedor.add(checkBox);


BoxLayout
Es como un FlowLayout, pero mucho más completo. Permite colocar los elementos en horizontal o vertical.
// Para poner en vertical
contenedor.setLayout(new BoxLayout(contenedor,BoxLayout.Y_AXIS));
contenedor.add(unBoton);
contenedor.add(unaEtiqueta);


GridLayout
Este pone los componentes en forma de matriz (cuadrícula), estirándolos para que tengan todos el mismo tamaño. El GridLayout es adecuado para hacer tableros, calculadoras en que todos los botones son iguales, etc.

// Creación de los botones
JButton boton[] = new JButton[9];
for (int i=0;i<9;i++)
   boton[i] = new JButton(Integer.toString(i));

// Colocación en el contenedor
contenedor.setLayout (new GridLayout (3,3));  // 3 filas y 3 columnas
for (int i=0;i<9;i++)
    contenedor.add (boton[i]);  // Añade los botones de 1 en 1.

BorderLayout
El BorderLayout divide la ventana en 5 partes: centro, arriba, abajo, derecha e izquierda.
Hará que los componentes que pongamos arriba y abajo ocupen el alto que necesiten, pero los estirará horizontalmente hasta ocupar toda la ventana.
Los componentes de derecha e izquierda ocuparán el ancho que necesiten, pero se les estirará en vertical hasta ocupar toda la ventana.
El componente central se estirará en ambos sentidos hasta ocupar toda la ventana.
El BorderLayout es adecuado para ventanas en las que hay un componente central importante (una tabla, una lista, etc) y tiene menús o barras de herramientas situados arriba, abajo, a la derecha o a la izquierda.
Este es el layout por defecto para los JFrame y JDialog.
contenedor.setLayout (new BorderLayout());
contenedor.add (componenteCentralImportante, BorderLayout.CENTER);
contenedor.add (barraHerramientasSuperior, BordeLayout.NORTH);
contenedor.add (botonesDeAbajo, BorderLayout.SOUTH);
contenedor.add (IndiceIzquierdo, BorderLayout.WEST);
contenedor.add (MenuDerecha, BorderLayout.EAST);

Por ejemplo, es bastante habitual usar un contenedor (JPanel por ejemplo) con un FlowLayout para hacer una fila de botones y luego colocar este JPanel en el NORTH de un BorderLayout de una ventana. De esta forma, tendremos en la parte de arriba de la ventana una fila de botones, como una barra de herramientas.
JPanel barraHerramientas = new JPanel();
barraHerrameientas.setLayout(new FlowLayout());
barraHerramientas.add(new JButton("boton 1"));
...
barraHerramientas.add(new JButton("boton n"));

JFrame ventana = new JFrame();
ventana.getContentPane().setLayout(new BorderLayout()); // No hace falta, por defecto ya es BorderLayout
ventana.getContentPane().add(barraHerramientas, BorderLayout.NORTH);
ventana.getContentPane().add(componentePrincipalDeVentana, BorderLayout.CENTER);

ventana.pack();
ventana.setVisible(true);


GridBagLayout
El GridBagLayout es de los layouts más versátiles y complejos de usar. Es como el GridLayout, pone los componentes en forma de matriz (cuadrícula), pero permite que las celdas y los componentes en ellas tengan tamaños variados.
§  Es posible hacer que un componente ocupe varias celdas
§  Un componente puede estirarse o no con su celda
§  Si no se estira, puede quedar en el centro de la celda o pegarse a sus bordes o esquinas.
§  Las columnas pueden ensancharse o no al estirar la ventana y la proporición podemos decidirla
§  Lo mismo con la filas.

CardLayout
El CardLayout hace que los componente recibidos ocupen el máximo espacio posible, superponiendo unos a otros. Sólo es visible uno de los componentes, los otros quedan detrás. Tiene métodos para indicar cual de los componentes es el que debe quedar encima y verse.
El CardLayout es el que utiliza el JTabbedPane (el de las pestañas) de forma que en función de la pestaña que pinchemos, se ve uno u otro.

SpringLayout
Para los nostálgicos que usaban motif, este layout es muy similar a los attachment de motif.
Se añaden los componentes y para cada uno de ellos tenemos que decir qué distancia en pixel queremos que tenga cada uno de sus bordes respecto al borde de otro componente. Por ejemplo, para decir que el borde izquierdo de una etiqueta está a 5 pixels del panel que la contiene ponemos
layout.putConstraint(SpringLayout.WEST, label, 5, SpringLayout.WEST, contentPane);
Para decir que el borde derecho de la etiqueta debe estar a 5 pixels del borde izquierdo de un JTextField, ponemos esto
layout.putConstraint(SpringLayout.WEST, textField, 5, SpringLayout.EAST, label);
Con este layout, cuando estiramos el panel, siempre ceden aquellos componentes más "flexibles". Entre una etiqueta y una caja de texto, la caja de texto es la que cambia su tamaño.


CONTENEDORES EN JAVA

Un contenedor es un componente especial que permite contener en su interior a otros componentes, incluidos otros contenedores. Un contenedor posee, además de la habilidad de contener otros componentes, la de organizar dichos componentes en su interior.
Los contenedores descienden de la clase padre Container, la cual, a su vez, es descendiente directa de la clase Component
java.lang.Object 
   java.awt.Component 
      java.awt.Container
Esta clase posee los métodos comunes a todos los contenedores, como aquellos que permiten agregar componentes al contenedor, localizar componentes, establecer la organización o disposición de los componentes (layouts), etc.
Para agregar componentes a un contenedor existe un método llamado add(añadir, agregar), que posee diversas sintaxis:
public Component add (Component comp)// Añade el componente al final del contenedor 
public Component add (String name,   // Añade el componente al final del contenedor y 
   Component comp)                   // le asigna un nombre 
public Component add (Component comp,// Añade el componente en la posicion indicada 
   int index)                        // por index. Con –1 lo añade al final 
public void add (Component comp,     // Añade el componente al final e indica al
   Object constraints)               // Layout las restricciones (x, y, ancho y alto) 
public void add (Component comp,     // Idem al anterior pero indicando que agregue 
   Object constraints,               // el componente en la posicion index dentro de 
   int index)                        // la lista de componentes del contenedor
Para eliminar componentes del contenedor se utiliza el método remove, cuya sintaxis es la siguiente:
public remove (int index)       // Elimina el componente que hay en la posicion index 
public remove (Component comp)  // Elimina el componente especificado
Si se desea vaciar el contenedor, el método removeAll es el método indicado:
public void removeAll ( )
Para conocer el número de componentes que hay actualmente insertados en el contenedor, se puede utilizar el método siguiente:
public int getComponentCount ( )
Obtener el componente situado en un posición específica en la lista de componentes del contenedor es muy sencillo. El método getComponent se encarga de ello:
public Component getComponent (int n)
Es posible también obtener el componente en relación a unas coordenadas específicas dentro del contenedor. Para ello, hay varios métodos:
public Component getComponentAt (int x, int y) 
public Component getComponentAt (Point p) 
public findComponentAt (int x, int y) 
public findComponentAt (Point p)
La diferencia entre el método getComponentAt y findComponentAt es quegetComponentAt busca en el hijo más inmediato, mientras quefindComponentAt puede buscar dentro de hijos anidados.
En este caso, si se desea conocer si un componente dado está contenido dentro del contenedor, existe un método:
public boolean isAncestorOf (Component c)
Este método retorna true si el componente está contenido dentro del contenedor, o false en caso contrario.
Los contenedores poseen la habilidad de pintar, y poseen varios métodos que sirven para pintar en el contenedor en un determinado momento:
public void paint (Graphics g)            // Cuando se repinta todo el contenedor 
public void update (Graphics g)           // Cuando se actualiza el contenedor 
public void print (Graphics g)            // Cuando se imprime el contenedor 
public void paintComponents (Graphics g)  // Cuando se repinta cada componente 
public void printComponents (Graphics g)  // Cuando se imprime cada componentes
Los contenedores poseen también la habilidad de validar o no su contenido. Esto se consigue gracias a algunos métodos especiales:
public void invalidate ( )   // Invalida el contenedor 
public void validate ( )     // Valida el contenedor y sus subcomponentes
Cuando se invalida un contenedor, éste y sus padres son marcados como que necesitan reorganizarse. Cuando se valida el contenedor, el contenedor y sus subcomponentes son reorganizados. El uso habitual de estos métodos se realiza cuando se han añadido, eliminado o modificado sus subcomponentes.
Los contenedores poseen la facultad de organizar sus subcomponentes gracias a los Layouts (disposiciones u organizadores). Para gestionar dichos organizadores existen varios métodos, algunos de los cuales ya se han visto en capítulos anteriores:
public void setLayout (LayoutManager mgr) // Establece el tipo de disposicion 
public LayoutManager getLayout ( )        // Obtiene el tipo de disposicion 
public void doLayout ( )                  // Reorganiza la disposicion del contenedor
Las disposiciones u organizadores serán tratados más adelante dentro de este mismo tema dedicado a los contenedores.
Todos los métodos aquí vistos son comunes a todos los tipos de contenedores. Como se indicó desde un principio, la clase Container es la clase padre de los contenedores, y pone a disposición de las clases más especializadas (cada tipo de contenedor) estos métodos y los heredados de la clase Component. 
 

Tipos de contenedores
En sí, la clase Container no se utiliza directamente, pues es una clase abstracta que define el comportamiento de los distintos tipos de contenedores. Sin embargo, sí es utilizada para extenderla y crear clases más especializadas. No obstante, Java posee varios tipos de contenedores, dispuestos en la siguiente jerarquía:
java.lang.Object 
   java.awt.Component 
      java.awt.Container 
         java.awt.Panel 
            java.applet.applet 
         java.awt.ScrollPane 
         java.awt.Window 
            java.awt.Dialog 
               java.awt.FileDialog 
            java.awt.Frame 
 

Panel
Un panel es la clase de contenedor más simple, ya que únicamente proporciona el espacio para contener otros componentes y contenedores. 
 

Applet
Un applet es un contenedor que deriva de la clase Panel. Además de proporcionar espacio para contener otros componentes, tiene la funcionalidad de ser una aplicación autónomo e independiente dentro de una página web. 
 

ScrollPane
Este tipo de contenedor puede implementar desplazamientos horizontales y/o verticales para poder acceder a posiciones de sus componentes hijos, si éstos no son visualizados. 
 

Window
Representa el nivel más básico de una ventana, sin bordes ni menús. Una instancia de una ventana no puede estar contenida dentro de otro contenedor. 
 

Dialog
Es una especialización de una ventana, y muestra bordes y título, y es utilizada comúnmente como formularios de entrada por los usuarios. 
 

FileDialog
Es una especialización de un diálogo (Dialog), permitiendo al usuario seleccionar navegar por los discos para seleccionar ficheros o archivos. 
 

Frame
Un frame (marco) es una especialización de ventana y muestra bordes y título, y además pueden tener menús. La diferencia con los diálogos es que éstos son dependientes de una ventana padre. Si esta ventana padre se minimiza, se restaura o se cierra, automáticamente los diálogos también se minimizan, se restauran o se cierran. Además, los diálogos pueden redimensionarse o moverse, pero no minimizarse ni maximizarse. 
 


Constructores
public Container ( ) 
 

Métodos
public Component add (Component comp) 
public Component add (String name, Component comp) 
public Component add (Component comp,int index) 
public void add (Component comp, Object constraints) 
public void add (Component comp, Object constraints, int index) 
public remove (int index) 
public remove (Component comp) 
public void removeAll ( ) 
public int getComponentCount ( ) 
public Component getComponent (int n) 
public Component getComponentAt (int x, int y) 
public Component getComponentAt (Point p) 
public findComponentAt (int x, int y) 


public findComponentAt (Point p) 
public boolean isAncestorOf (Component c) 
public void paint (Graphics g) 
public void update (Graphics g) 
public void print (Graphics g) 
public void paintComponents (Graphics g) 
public void printComponents (Graphics g) 
public void invalidate ( ) 
public void validate ( ) 
public void setLayout (LayoutManager mgr) 
public LayoutManager getLayout ( ) 
public void doLayout ( )