|
![]() |
AWT - Layouts (II) |
Anterior | Siguiente |
Este es el tipo de composición que se utiliza cuando se necesita una zona de la ventana que permita colocar distintos Componentes en esa misma zona. Este layout suele ir asociado con botones de selección (Choice), de tal modo que cada selección determina el panel (grupo de componentes) que se presentarán. En el ejemplo java1327.java, con el que se ilustra el uso de este tipo de controlador de posicionamiento, se creará u objeto interfaz de usuario basado en un objeto Frame, que contiene a los dos objetos Panel que lo conforman. Uno de los objetos panel servirá como pantalla sobre la que presentar las diversas fichas (cards), utilizando el CardLayout. El otro Panel contiene varios objetos Button que se pueden utilizar para cambiar las fichas que se presentan en el Panel contrario. El programa es un poco más complejo que los ejemplos anteriores de los controladores de posición, debido a que el CardLayout también es más complicado, pero por el contrario, esta complejidad le proporciona un considerable poder y funcionalidad a la hora de su empleo. Todos los botones de las fichas son pasivos, no tienen registrados controladores de eventos, excepto uno de ellos, que contiene un botón sobre el cual, al pulsarlo, se presenta la fecha y la hora del sistema en esa misma ficha. Esto se consigue a través de objetos de tipo ActionListener que funcionan a dos niveles. En un primer nivel, los objetos ActionListener se utilizan para seleccionar la ficha que se visualizará. Y en un segundo nivel, se registra un objeto ActionListener para uno de los botones de una de las fichas, que hará que aparezca en pantalla la fecha y la hora, siempre que la ficha que contiene el botón esté visible y se pulse ese botón. La visualización de esta ficha en la ventana, durante la ejecución del ejemplo, es la misma imagen que se reproduce en la siguiente figura. En el programa, se crea un objeto Frame a un nivel superior del interfaz, que contiene dos objetos Panel. Uno de los objetos Panel es el panel de visualización que utiliza el programa para presentar cada una de las siguientes fichas, que se añaden al Panel utilizando el CardLayout como controlador de posicionamiento:
Todos los Componentes de cada una de las fichas son pasivos, no tienen ningún controlador de eventos registrado sobre ellos; excepto la ficha identificada como panelFecha, que consiste en un objeto panel al que se han incorporado un objeto Button y un objeto Label. Esta ficha, o tarjeta, no es pasiva, porque hay un objeto ActionListener instanciado y registrado sobre el botón que presenta la fecha y la hora en el objeto Label de esta misma ficha. El otro Panel, que contiene el objeto Frame principal, es el panel de control, que contiene a su vez a cinco botones, cuatro de desplazamiento y otro que va a permitir la visualización directa de la ficha en que se presenta la fecha y la hora. Todos los botones son activos, ya que disponen de controladores de eventos registrados sobre ellos, y la acción que realizan es la que indica su etiqueta; por ejemplo, si se pulsa el botón "siguiente", aparecerá en la ventana la siguiente ficha a la que se esté visualizando en ese momento. Además, se instancia y registra un objeto receptor de eventos windowClosing() sobre el Frame para concluir la ejecución del programa cuando el usuario pulsa el botón de cierre del Frame. Ahora se discuten las partes más interesantes del código del programa que, al ser más complejo que los vistos en secciones anteriores, también tiene más trozos de código que merecen un comentario. El primer trozo de código interesante que se encuentra en la visión del programa es el usado para crear una cualquiera de las fichas, que contiene un objeto Button y un objeto Label, y que va a componer posteriormente el Panel que permitirá visualizar la fecha y la hora. Sobre el objeto Button se instancia y registra un objeto receptor de eventos de tipo ActionListener. El método sobrescrito actionPerformed() en el objeto ActionListener, hace que la fecha y la hora aparezcan en el objeto Label que está sobre la ficha, cuando se pulsa sobre el botón. Label labelFecha = new Label(" "); Button botonFecha = new Button( "Fecha y Hora" ); Panel panelFecha = new Panel(); panelFecha.add( botonFecha ); panelFecha.add( labelFecha ); // Se instancia y registra un objeto ActionListener sobre el // boton que va a presentar la fecha y la hora botonFecha.addActionListener( new ActionListernerFecha( labelFecha ) ); Las siguientes sentencias son las que permiten la creación de las fichas del panel de visualización. Primero se instancia un objeto de tipo CardLayout y se asigna a una variable de referencia, ya que no puede ser un objeto anónimo, porque se va a necesitar a la hora de procesar los eventos. CardLayout miCardLayout = new CardLayout(); A continuación, se instancia el objeto Panel correspondiente al panel de visualización de las fichas, se especifica el controlador de posicionamiento de los Componentes y se cambia su fondo para que sea de color amarillo y se pueda distinguir fácilmente del otro objeto Panel que se usa en el interfaz. Panel panelPresentacion = new Panel(); // Se fija el Cardlayout para el objeto panel panelPresentacion.setLayout( miCardLayout ); panelPresentacion.setBackground( Color.yellow ); Una vez que ya existe el objeto Panel para la visualización, el siguiente paso es añadir fichas al Panel utilizando el CardLayout. En las llamadas al método add() de la clase Container de las sentencias que se reproducen, el primer parámentro es el objeto que se añade, y el segundo parámentro es el nombre del objeto. Todos los objetos que se añaden son anónimos porque son pasivos, excepto el objeto panelFecha, que se ha construido anteriormente y que no puede ser anónimo al estar compuesto por un objeto Panel, un objeto Button y un objeto Label, y se necesita una variable de referencia del Panel para poder instanciarlo. El nombre del objeto especificado como cadena en el segundo parámetro del método add(), se utilizará como parámentro del método show(), para indicar cual de las fichas será la que se visualice. panelPresentacion.add( new Button( "La Primera ficha es un Boton" ),"primero" ); panelPresentacion.add( new Label( "La Segunda ficha es un Label" ),"segundo" ); panelPresentacion.add( new Label( "Tercera ficha, tambien un Label" ),"tercero" ); panelPresentacion.add( new Label( "Cuarta ficha, de nuevo un label" ),"cuarto" ); panelPresentacion.add( panelFecha,"panel fecha" ); panelPresentacion.add( new TextField( "La Ultima ficha es un campo de texto" ),"sexto" ); La siguiente tarea es ya la construcción del panel de control. Las dos sentencias que siguen son ya conocidas, y sirven para instanciar los objetos Button del panel de control y registrar objetos ActionListener para que recojan los eventos que se produzcan sobre ellos. Button botonSiguiente = new Button( "Siguiente" ); . . . botonPrimero.addActionListener( new ListenerPrimero( miCardLayout,panelPresentacion ) ); Las dos líneas de código mostradas a continuación, también resultarán ya conocidas, y aquí se utilizan para instanciar el objeto del panel de control y colocar los cinco botones sobre él. Panel panelControl = new Panel(); panelControl.add( botonPrimero ); Ahora se colocan los dos paneles sobre el Frame que constituye la parte principal del interfaz de usuario utilizando un BorderLayout y colocándolos en posiciones superior e inferior. Frame miFrame = new Frame( "Tutorial de Java, AWT" ); // Le incorporamos el panel de visualizacion y el panel de control // para crear un objeto mas complejo miFrame.add( panelPresentacion,"North" ); miFrame.add( panelControl,"South" ); En este momento ya está creada la clase correspondiente al interfaz de usuario y lo que resta es definir las clases que van a recibir los eventos, para lo cual se instancian y registran objetos receptores que contienen el método actionPerformed() sobrescrito. La primera clase ActionListener es la que se usa para dar servicio a los eventos provocados por la pulsación sobre el obobjeto Button colocado sobre la ficha que presenta la fecha y la hora. Esta clase contiene una variable de instancia, un constructor y el método actionPerformed() sobrescrito. La parte más interesante es el código del método sobrescrito actionPerformed(), tal como muestra el siguiente trozo de código: public void actionPerformed( ActionEvent evt ) { miObjLabel.setText( new Date().toString() ); } Como se puede observar, este método instancia un nuevo objeto de la clase Date, que cada vez que es instanciado contiene información que puede utilizarse para obtener la fecha y hora en que fue instanciado. Esta información se extrae utilizando el método toString() y luego, se emplea el método setText() de la clase Label para depositar la fecha y hora en el objeto Label. Esta clase es seguida de cinco clases ActionListener muy semejantes entre ellas, que se utilizan para cambiar la ficha que se presenta en pantalla. Cuando cualquiera de los botones del panel de control es pulsado. Las definiciones de las clases difieren únicamente en el método que se llama dentro del método sobrescrito actionPerformed(). El resto del código no es de especial interés, y ya es suficiente (esperemos), con los comentarios situados en el código fuente del ejemplo, para que el lector tome posición correcta ante el entendimiento de ese código. Los Componentes se pueden colocar en Contenedores utilizando cualquiera de los controladores de posicionamiento, o utilizando posicionamiento absoluto para realizar esta función. La primera forma de colocar los Componentes se considera más segura porque automáticamente serán compensadas las diferencias que se puedan encontrar entre resoluciones de pantalla de plataformas distintas. La clase Component proporciona métodos para especificar la posición y tamaño de un Componente en coordenadas absolutas indicadas en pixels: setBounds( int,int,int,int ); setBounds( Rectangle ); La posición y tamaño si se especifica en coordenadas absolutas, puede hacer más difícil la consecución de una apariencia uniforme, en diferentes plataformas, del interfaz, según algunos autores; pero, a pesar de ello, es interesante saber cómo se hace. La siguiente aplicación, java1328.java, coloca un objeto Button y un objeto Label sobre un objeto Frame, utilizando coordenadas absolutas en pixels. El programa está diseñado para ser lo más simple posible y no contiene ningún controlador de eventos, por lo que el botón de cerrar la ventana colocado sobre el Frame no es funcional, y hay que concluir a las bravas la aplicación. El objeto Button y el objeto Label, de color amarillo, se colocan sobre un objeto Frame, de tal forma que la posición y tamaño indicados para los Componentes hacen que los dos se superpongan. Este es uno de los potenciales problemas que se producen con el uso de coordenadas absolutas; aunque, por supuesto, a menos sobre una determinada plataforma, con un cuidadoso diseño. Si se cambia el tamaño del Frame, el botón y la etiqueta permanecen en el mismo tamaño y posición. El Frame puede ser cambiado de tamaño hasta el punto de no poder ver los dos Componentes, y esto no es agradable para ningún usuario. import java.awt.*; import java.awt.event.*; public class java1328 { 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 rectagulo 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 ); } } El resultado en pantalla de la ejecución del programa se muestra en la imagen que se reproduce a continuación. Acto seguido se entra un poco más a fondo en el programa anterior, para aclarar las partes más interesantes del código. Por ejemplo, las siguientes dos sentencias: Button miBoton = new Button( "Boton" ); // Al rectágulo se le pasan los parámetros: x,y,ancho,alto miBoton.setBounds( new Rectangle( 25,20,100,75 ) ); lo que hacen en realidad es crear un objeto Button con el rótulo "Boton", indicar que su posición en el eje X es 25 y en el eje Y es 50, medidas en pixels desde la esquina superior izquierda del Contenedor del objeto, teniendo en cuenta que el eje Y crece hacia abajo. El punto de referencia del botón es su esquina superior-izquierda; finalmente, especifica una anchura de 100 pixels y una anchura de 75 pixels para el botón. En este punto del programa, una vez ejecutadas estas dos sentencias, todavía no se ha identificado el objeto que va a contener el Botón, por lo tanto, setBountds() proporciona información de las coordenadas absolutas donde se colocará el botón en cualquiera que sea el Contenedor que lo contenga. El método setBounds() es un método de la clase Component y por lo tanto, heredado en todas las subclases de Component, incluida la clase Button. El método indica que el Componente tendrá un tamaño y estará posicionado de acuerdo a una caja o rectángulo, cuyos lados son paralelos a los ejes X e Y. Hay dos versiones sobrecargadas de setBounds(). Una de ellas permite que tamaño y posición se indiquen a través de cuatro enteros: ordenadas, abscisas, ancho y alto. La otra versión requiere que se utilice un objeto Rectangle, que se pasa como parámetro al método. Esta última versión es más flexible, porque la clase Rectangle tiene siete constructores diferentes que pueden ser utilizados para crear la caja que indicará la posición y tamaño del componente. Las siguientes líneas de código crean un objeto Label con fondo amarillo y, de nuevo, se utiliza el método setBounds() para posicionarlo: Label miEtiqueta = new Label( "Tutorial de Java" ); miEtiqueta.setBounds( new Rectangle( 100,75,100,75 ) ); miEtiqueta.setBackground( Color.yellow ); El método setBackground() también es un método de la clase Component heredado por todas sus subclases. Este método requiere un parámetro de tipo Color, que se puede crear de varias formas, pero la más simple es referirse a una de las constantes que están definidas en la clase Color, utilizando sintaxis del tipo: public final static Color yellow; Hay que recordar que a las variables declaradas como estáticas en una clase se puede acceder utilizando el nombre de la clase y el nombre de la variable. Haciendo las variables de tipo final, lo que se consiguen son en realidad, constantes. Hay unos treinta colores diferentes definidos de este modo, si se necesita un color distinto, no incluido en esta lista, se puede utilizar uno de los constructores sobrecargados que permiten especificar un color en función de la cantidad de rojo, verde y azul que intervienen en su composición. Las siguientes líneas de código: Frame miFrame = new Frame( "Tutorial de Java, AWT" ); miFrame.setLayout( null ); crean un objeto Frame con un título. Este objeto Frame, o marco, es el tipo de objeto que los usuarios de Windows consideran como una típica Ventana. Se puede cambiar de tamaño y tiene cuatro botones: un botón de control o de menú de ventana en la parte superior izquierda y, en la zona superior-derecha, dispone de tres botones para minimizar, maximizar y cerrar la ventana. En este ejemplo no se acepta el layout de tipo BorderLayout, que por defecto proporciona el sistema, sino que se especifican posición y tamaño de los Componentes en coordenadas absolutas. Por ello, será necesario sobreescribir la especificación del layout con null. Este parámetro se le pasa al método setLayout() de la clase Container de la que Frame es una subclase, y que espera como parámetro un objeto de la clase LayoutManager, o null, para indicar que no se va a utilizar ningún manejador de composición, como en este caso. Las dos líneas que aparecen a continuación: miFrame.add( miBoton ); miFrame.add( miEtiqueta ); hacen que los dos componentes que previamente se habían definido entren a formar parte de la composición visual utilizando para ello el método add() de la clase Container. Este método tiene varias versiones sobrecargadas, y aquí se utiliza la que requiere un objeto de tipo Component como parámetro; y como Button y Label son subclases de la clase Component, están perfectamente cualificados para poder ser parámetros del método add(). Una vez que se ha llegado a este punto, ya solamente falta fijar el tamaño que va a tener el marco en la pantalla y hacerlo visible, cosa que hacen las dos líneas de código que se reproducen a continuación: miFrame.setSize( 250,175 ); miFrame.setVisible( true ); |
|