|
AWT - Contenedores |
Anterior | Siguiente |
La clase Container es una clase abstracta derivada de Component, que representa a cualquier componente que pueda contener otros componentes. Se trata, en esencia, de añadir a la clase Component la funcionalidad de adición, sustracción, recuperación, control y organización de otros Componentes. Al igual que la clase Component, no dispone de constructores públicos y, por lo tanto, no se pueden instanciar objetos de la clase Container. Sin embargo, sí se puede extender para implementar la nueva característica incorporada a Java en el JDK 1.1, de los componentes Lightweight. El AWT proporciona varias clases de Contenedores:
Aunque los que se pueden considerar como verdaderos Contenedores con Window, Frame, Dialog y Panel, porque los demás son subtipos con algunas características determinadas y solamente útiles en circunstancias muy concretas. Es una superficie de pantalla de alto nivel (una ventana). Una instancia de la clase Window no puede estar enlazada o embebida en otro Contenedor. El controlador de posicionamiento de Componentes por defecto, sobre un objeto Window, es el BorderLayout. Una instancia de esta clase no tiene ni título ni borde, así que es un poco difícil de justificar su uso para la construcción directa de un interfaz gráfico, porque es mucho más sencillo utilizar objetos de tipo Frame o Dialog. Dispone de varios métodos para alterar el tamaño y título de la ventana, o los cursores y barrar de menús. Es una superficie de pantalla de alto nivel (una ventana) con borde y título. Una instancia de la clase Frame puede tener una barra de menú. Una instancia de esta clase es mucho más aparente y más semejante a lo que se entiende por ventana. Y, a no ser que el lector haya comenzado su estudio por esta página, ya se habrá encontrado en varias ocasiones con la clase Frame, que es utilizada en gran parte de los ejemplos de este Tutorial. Su uso se debe en gran parte a la facilidad de su instanciación y, lo que tampoco deja de ser interesante, su facilidad de conclusión. La clase Frame extiende a la clase Window, y su controlador de posicionamiento de Componentes por defecto es el BorderLayout. Los objetos de tipo Frame son capaces de generar varios tipos de eventos, de los cuales el más interesante es el evento de tipo WindowClosing, que se utiliza en este Tutorial de forma exhaustiva, y que se produce cuando el usuario pulsa sobre el botón de cerrar colocado en la esquina superior-derecha (normalmente) de la barra de título del objeto Frame. En el ejemplo java1312.java se ilustra el uso de la clase Frame y algunos de sus métodos. El programa instancia un objeto Frame con tres botones que realizan la acción que se indica en su título. La imagen reproduce la ventana que genera la aplicación y su situación tras haber pulsado el botón que cambia el cursor a forma de mano. Es un ejemplo muy simple, aunque hay que advertir al lector que se hace uso en él de la sintaxis abreviada de las clases anidadas, que se tratarán en otra sección; para que no se asuste al ver el código del ejemplo. Este método se utiliza para instanciar y registrar receptores de eventos sobre los tres botones, más el de cerrar la ventana, colocados sobre el objeto Frame. import java.awt.*; import java.awt.event.*; import java.util.*; public class java1312 { public static void main( String args[] ) { IHM ihm = new IHM(); } } class IHM { Frame miFrame; public IHM() { // Se instancian tres botones con textos indicando lo que // hacen cuando se pulse sobre ellos Button botonTitulo = new Button( "Imprime Titulo" ); Button botonCursorMano = new Button( "Cursor Mano" ); Button botonCursorFlecha = new Button( "Cursor Flecha" ); // Instancia un objeto Frame con su titulo indicativo de que se // se trata, utilizando un FlowLayout miFrame = new Frame( "Tutorial de Java, AWT" ); miFrame.setLayout( new FlowLayout() ); // Añade tres objetos Button al Frame miFrame.add( botonTitulo ); miFrame.add( botonCursorMano ); miFrame.add( botonCursorFlecha ); // Fija el tamaño del Frame y lo hace visible miFrame.setSize( 250,200 ); miFrame.setVisible( true ); // Instancia y registra objetos ActionListener sobre los // tres botones utilizando la sintaxis abreviada de las // clases anidadas botonTitulo.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { System.out.println( miFrame.getTitle() ); } } ); botonCursorMano.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent evt ) { miFrame.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); } } ); botonCursorFlecha.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { miFrame.setCursor( new Cursor( Cursor.DEFAULT_CURSOR ) ); } } ); // Instancia y registra un objeto WindowListener sobre el objeto // Frame para terminar el programa cuando el usuario haga click // con el raton sobre el boton de cerrar la ventana que se // coloca sobre el objeto Frame miFrame.addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent evt ) { // Concluye la aplicacion cuando el usuario cierra la // ventana System.exit( 0 ); } } ); } } El siguiente trozo de código es el típico utilizado en la instanciación de un objeto Frame, la indicación del controlador de posicionamiento de Componentes que se va a utilizar y, en este caso, la incorporación de los tres Componentes de tipo Button al objeto Frame. // Se instancian tres botones con textos indicando lo que // hacen cuando se pulse sobre ellos Button botonTitulo = new Button( "Imprime Titulo" ); Button botonCursorMano = new Button( "Cursor Mano" ); Button botonCursorFlecha = new Button( "Cursor Flecha" ); // Instancia un objeto Frame con su titulo indicativo de que se // se trata, utilizando un FlowLayout miFrame = new Frame( "Tutorial de Java, AWT" ); miFrame.setLayout( new FlowLayout() ); // Añade tres objetos Button al Frame miFrame.add( botonTitulo ); miFrame.add( botonCursorMano ); miFrame.add( botonCursorFlecha ); Y en los dos bloques de sentencias que se reproducen a continuación, se utilizan clases anidadas para instanciar y registrar objetos de tipo ActionListener. Por ejemplo, sobre el botón que permite recoger el título de la ventana, se hace tal como se indica. botonTitulo.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { System.out.println( miFrame.getTitle() ); } } ); Y, para instanciar y registrar un objeto WindowListener sobre el objeto Frame, para concluir la aplicación cuando el usuario cierre la ventana, se emplean las siguientes líneas de código. miFrame.addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent evt ) { // Concluye la aplicación cuando el usuario cierra la // ventana System.exit( 0 ); } } ); Es una superficie de pantalla de alto nivel (una ventana) con borde y título, que permite entradas al usuario. La clase Dialog extiende la clase Window, que extiende la clase Container, que extiende a la clase Component; y el controlador de posicionamiento por defecto es el BorderLayout. De los constructores proporcionados por esta clase, destaca el que permite que el diálogo sea o no modal. Todos los constructores requieren un parámetro Frame y, algunos de ellos, permiten la especificación de un parámetro booleano que indica si la ventana que abre el diálogo será modal o no. Si es modal, todas las entradas del usuario serán recogidas por esta ventana, bloqueando cualquier entrada que se pudiese producir sobre otros objetos presentes en la pantalla. Posteriormente, si no se ha especificado que el diálogo sea modal, se puede hace que adquiera esta característica invocando al método setModal(). El ejemplo java1313.java, cuya imagen en pantalla al arrancar es la que reproduce la imagen que precede a este párrafo, presenta el mínimo código necesario para conseguir que un objeto Dialog aparezca sobre la pantalla. Cuando se arranca el programa, en la pantalla se visualizará un objeto Frame y un objeto Dialog, que debería tener la mitad de tamaño del objeto Frame y contener un título y un botón de cierre. Este botón de cierre no es operativo. El objeto Dialog no tiene la caja de control de la esquina superior izquierda al uso. El objeto Dialog puede ser movido y redimensionado, aunque no se puede ni minimizar ni maximizar. Se pueden colocar en cualquier lugar de la pantalla, su posición no está restringida al interior del padre, el objeto Frame. El objeto Dialog difiere significativamente en apariencia del objeto Frame, sobre todo por la presencia del borde en este último, circunstancia que llama mucho la atención. import java.awt.*; import java.awt.event.*; public class java1313 extends Frame { public static void main( String args[] ) { // Instancia un objeto de este tipo new java1313(); } // Constructor public java1313() { setTitle( "Tutorial de Java, AWT" ); setSize( 250,150 ); setVisible( true ); Dialog miDialogo = new Dialog( this,"Dialogo" ); miDialogo.setSize( 125,75 ); // Hace que el dialogo aparezca en la pantalla miDialogo.show(); } } La parte más interesante del ejemplo reside en tres sentencias. La primera, instancia el objeto Dialog como hijo del objeto principal, this . La segunda sentencia establece el tamaño inicial del objeto Dialog. La tercera sentencia hace que el objeto Dialog aparezca en la pantalla. Dialog miDialogo = new Dialog( this,"Dialogo" ); miDialogo.setSize( 125,75 ); // Hace que el dialogo aparezca en la pantalla miDialogo.show(); El ejemplo java1314.java, está diseñado para producir dos objetos Dialog, uno modal y otro no-modal. Un objeto Frame sirve de padre a los dos objetos Dialog. El Dialog no-modal se crea con un botón que sirve para cerrarlo. Sobre este botón se instancia y registra un objeto ActionListener. Este objeto ActionListener es instanciado desde una clase ActionListener compartida. El código del método sobrescrito actionPerformed() de la clase ActionListener, cierra el objeto Dialog, invocando al método setVisible() con el parámetro false , aunque también se podría haber utilizado hide() o dispose(). El Dialog modal se crea de la misma forma, conteniendo un botón al que se asigna un cometido semejante. Sobre el objeto Frame se crean dos botones adicionales, uno mostrará el diálogo modal y el otro mostrará el diálogo no-modal. Estos dos objetos Button comparten una clase ActionListener que está diseñada para mostrar un objeto Dialog de un tamaño predeterminado y en una posición parametrizada, controlada a través del parámetro offset, que se pasa al objeto ActionListener cuando se instancia. La imagen muestra la ventana que genera la aplicación cuando se ejecuta por primera vez y se selecciona el Diálogo No-Modal, pulsando el botón correspondiente. Para evitar que se superpongan, los objetos ActionListener de los dos botones que visualizan los objetos Dialog, se instancian con valores de offset diferentes, por lo que aparecerán en posiciones distintas de la pantalla en el momento de su visualización. Si se compila y ejecuta el programa, aparecerán los dos botones en la pantalla, tal como muestra la imagen que aparece en párrafos anteriores. Uno de los botones puede ser utilizado para mostrar el objeto Dialog no-modal y el otro para visualizar el Dialog modal. Cuando el objeto Dialog modal no está visible, se podrá mostrar y cerrar el objeto Dialog no-modal, o pulsar en la caja de cierre del Frame para terminar la ejecución del programa. Sin embargo, cuando está visible el Dialog modal, no se podrá realizar ninguna otra acción dentro del programa; el modo de operación es el que se conoce como aplicación modal. Un objeto receptor de eventos windowClosing() es instanciado y registrado sobre el Frame para concluir la ejecución del programa cuando se cierre el Frame; sin embargo, el Frame no puede cerrarse cuando el objeto Dialog modal está visible. A continuación se comentan los trozos de código más interesantes del ejemplo anterior. El primero de ellos es la sentencia que instancia el objeto Frame, que a pesar de ser semejante a muchas de las ya vistas, lo que la hace importante en este programa es el ser padre de los dos objetos Dialog, por lo cual es imprescindible que sea instanciado antes de los dos objetos Dialog. Frame miFrame = new Frame( "Tutorial de Java, AWT" ); El siguiente fragmento interesante es el típico código que es utilizado para instanciar los objetos Dialog, colocar un objeto Button en el objeto Dialog, e instanciar y registrar un objeto ActionListener sobre el objeto Button. Dialog dialogoNoModal = new Dialog( miFrame,"Dialogo No Modal" ); Button botonCerrarNoModal = new Button( "Cerrar" ); dialogoNoModal.add( botonCerrarNoModal ); botonCerrarNoModal.addActionListener( new closeDialogListener( dialogoNoModal ) ); Este es el fragmento que crea el objeto Dialog modal. El que se usa para crear el objeto Dialog no-modal es esencialmente el mismo excepto que no tiene el parámetro booleando true en la invocación del constructor. Este código es seguido por el que instancia los objetos Button y registra objetos ActionListener sobre estos botones. Y, a este código le sigue el que finaliza la construcción del objeto Frame, que ya se ha visto en varias ocasiones y no merece la pena insistir en él. El trozo de código más interesante de todos es el fragmento en que la clase ActionListener es utilizada para instanciar objetos para mostrar un objeto Dialog. Y es muy interesante por dos aspectos. Uno de ellos es el constructor parametrizado que se utiliza para guardar el offset que se le pasa como parámetro al objeto ActionListener, cuando se instancia. Este valor de offset es combinado con valores en el código para controlar el tamaño y la posición del objeto Dialog cuando aparece en la pantalla. El otro aspecto interesante es que el método show() de la clase Dialog es utilizado para hacer aparecer el objeto Dialog en la pantalla. class showDialogListener implements ActionListener { Dialog oDialog; int oOffset; showDialogListener( Dialog dialogo,int offset ) { oDialog = dialogo; oOffset = offset; } public void actionPerformed( ActionEvent evt ) { // Seguir este orden es critico para un dialogo modal oDialog.setBounds( oOffset,oOffset,150,100 ); oDialog.show(); } } El orden de ejecución de las sentencias dentro del método actionPerformed() es crítico. Si el método show() se ejecuta antes del método setBounds() sobre el objeto Dialog modal, el método setBounds() utilizado para controlar el tamaño y posición del objeto Dialog, no tendría efecto alguno. Tamaño y posición del diálogo deben establecerse antes de hacerlo visible. El último fragmento de código interesante es el método sobrescrito actionPerformed() utilizado para cerrar los objetos Dialog. En este ejemplo se usa la llamada al método setVisible() con el parámetro false, aunque también se podría haber utilizado el método hide() o el método dispose(), con el mismo cometido. public void actionPerformed( ActionEvent evt ) { oDialog.setVisible( false ); } La clase Panel es un Contenedor genérico de Componentes. Una instancia de la clase Panel, simplemente proporciona un Contenedor al que ir añadiendo Componentes. El controlador de posicionamiento de Componentes sobre un objeto Panel, por defecto es el FlowLayout; aunque se puede especificar uno diferente en el constructor a la hora de instanciar el objeto Panel, o aceptar el controlador de posicionamiento inicialmente, y después cambiarlo invocando al método setLayout(). Panel dispone de un método addNotify(), que se utiliza para crear un observador general (peerPerr) del Panel. Normalmente, un Panel no tiene manifestación visual alguna por sí mismo, aunque puede hacerse notar fijando su color de fondo por defecto a uno diferente del que utiliza normalmente. El ejemplo java1315.java, ilustra la utilización de objetos Panel para configurar un objeto de tipo interfaz gráfica, o interfaz hombre-máquina, incorporando tres objetos Panel a un objeto Frame. El controlador de posicionamiento de los Componentes para el objeto Frame se especifica concretamente para que sea un FlowLayout, y se alteran los colores de fondo de los objetos Panel para que sean claramente visibles sobre el Frame. Sobre cada uno de los paneles se coloca un objeto, utilizando el método add(), de tal modo que se añade un objeto de tipo campo de texto sobre el Panel de fondo amarillo, un objeto de tipo etiqueta sobre el Panel de fondo rojo y un objeto de tipo botón sobre el Panel de fondo azul. Ninguno de los Componentes es activo, ya que no se instancian ni registran objetos receptores de eventos sobre ellos. Así, por ejemplo, el único efecto que se puede observar al pulsar el botón del panel azul, se limita al efecto visual de la pulsación. Sin embargo, sí se instancia y registra un receptor de eventos sobre el Frame, para recoger la intención del usuario de cerrar la ventana y terminar la ejecución de la aplicación. import java.awt.*; import java.awt.event.*; public class java1315 { public static void main( String args[] ) { IHM ihm = new IHM(); } } class IHM { public IHM() { // Se construyen tres Paneles con fondos de color diferente // y sin contener ningun elemento activo Panel panelIzqdo = new Panel(); panelIzqdo.setBackground( Color.yellow ); panelIzqdo.add( new TextField( "Panel Izquierdo -> amarillo" ) ); Panel panelCentral = new Panel(); panelCentral.setBackground( Color.red ); panelCentral.add( new Label( "Panel Central -> rojo" ) ); Panel panelDrcho = new Panel(); panelDrcho.setBackground( Color.blue ); panelDrcho.add( new Button( "Panel Derecho -> azul" ) ); // Se instancia un objeto Frame utilizando un FlowLayout y // se colocan los tres objetos Panel sobre el Frame Frame miFrame = new Frame( "Tutorial de Java, AWT" ); miFrame.setLayout( new FlowLayout() ); miFrame.add( panelIzqdo ); miFrame.add( panelCentral ); miFrame.add( panelDrcho ); miFrame.setSize( 500,200 ); miFrame.setVisible( true ); miFrame.addWindowListener( new Conclusion() ); } } class Conclusion extends WindowAdapter { public void windowClosing( WindowEvent evt ) { // Concluye la aplicacion cuando el usuario cierra la ventana System.exit( 0 ); } } Las sentencias de código más interesantes del ejemplo se limitan a la típica instanciación de los tres objetos Panel, al control del color de fondo y a la incorporación de otro Componente al Panel, tal como se reproduce en las siguientes sentencias. Panel panelIzqdo = new Panel(); panelIzqdo.setBackground( Color.yellow ); panelIzqdo.add( new TextField( "Panel Izquierdo -> amarillo" ) ); Las siguientes líneas de código instancian un objeto Frame y le añaden los tres objetos Panel construidos anteriormente. Frame miFrame = new Frame( "Tutorial de Java, AWT" ); miFrame.setLayout( new FlowLayout() ); miFrame.add( panelIzqdo ); miFrame.add( panelCentral ); miFrame.add( panelDrcho ); Con este trozo de código se genera el objeto de más alto nivel de la aplicación para el interfaz de usuario. Añadir Componentes a un Contenedor Para que un interfaz sea útil, no debe estár compuesto solamente por Contenedores, éstos deben tener Componentes en su interior. Los Componentes se añaden al Contenedor invocando al método add() del Contenedor. Este método tiene tres formas de llamada que dependen del manejador de composición o layout manager que se vaya a utilizar sobre el Contenedor. En el código siguiente, java1316.java, se incorporan dos botones al Contenedor de tipo Frame. La creación se realiza en el método init() porque éste siempre es llamado automáticamente al inicializarse el applet. De todos modos, al inciarse la ejecución se crean los botones, ya que el método init() es llamado tanto por el navegador como por el método main(). import java.awt.*; public class java1316 extends java.applet.Applet { public void init() { add( new Button( "Uno" ) ); add( new Button( "Dos" ) ); } public static void main( String args[] ) { Frame f = new Frame( "Tutorial de Java" ); java1316 ejemplo = new java1316(); ejemplo.init(); f.add( "Center",ejemplo ); f.pack(); f.show(); } } El ejemplo también muestra la forma de cómo el código puede ejecutarse tanto como aplicación, utilizando el intérprete de Java, como desde un navegador, funcionando como cualquiera de los applets que se han visto en ejemplos anteriores. En ambos casos el resultado, en lo que al ejemplo se refiere, es el mismo: aparecerán dos botones en el campo delimitado por el Contenedor Frame. Los Componentes añadidos a un objeto Container entran en una lista cuyo orden define el orden en que se van a presentar los Componentes sobre el Contenedor, de atrás hacia delante. Si no se especifica ningún índice de orden en el momento de incorporar un Componente al Contenedor, ese Componente se añadirá al final de la lista. Hay que tener esto muy en cuenta, sobre todo a la hora de construir interfaces de usuario complejas, en las que pueda haber Componentes que solapen a otros Componentes o a parte de ellos. |
|