Tutorial de Java

Eventos en Swing

Anterior | Siguiente

Ahora se va a introducir el modelo de eventos en Swing, que es el modelo de Delegación. Los Componentes Swing no soportan el modelo de eventos de propagación, sino solamente el modelo de Delegación incluido desde el JDK 1.1; por lo tanto, si se van a utilizar Componentes Swing, se debe programar exclusivamente en el nuevo modelo. Aunque hay Componentes de Swing que se corresponden (o reemplazan) a componentes de AWT, hay otros que no tienen una contrapartida en el AWT. Esta circunstancia se reflejar en los dos ejemplos que se verán; en uno se hace una sustitución de Componentes y en el otro ya no hay contrapartida en el AWT.

Desde el punto de vista del manejo de eventos, los Componentes de Swing funcionan del mismo modo que los Componentes del AWT, a diferencia de los nuevos eventos que incorpora Swing. Desde otros puntos de vista, los Componentes Swing pueden parecerse ya más o menos a su contrapartida del AWT. Además, hay una serie de Componentes muy útiles en Swing, como son los árboles, la barra de progreso, los tooltips, que no tienen ningún Componente que se les pueda equiparar en el AWT.

En esta sección, solamente se va a tratar los Componentes Swing desde el punto de vista del control de eventos, dejando para otras secciones el estudio de los Componentes en sí mismos y de las características que aportan a Java.

En el ejemplo java1116.java, que es equivalente al ejemplo java1101.java, solamente se reemplaza cada instancia de Frame por una instancia de JFrame, además de incorporar la sentencia import que hace que las clases sean accesibles al compilador y al intérprete. Por lo demás, el control de eventos en este nuevo ejemplo es el mismo que se ejercía en el ejemplo del AWT (si el lector ha entrado directamente en esta sección, quizá fuese conveniente que volviese atrás y le echase un vistazo a la anterior).

En el ejemplo se puede comprobar la utilización de fuentes de eventos, receptores de eventos y adaptadores del Modelo de Delegación de Eventos para Componentes Swing. Sucintamente, la aplicación instancia un objeto que crea un interfaz de usuario consistente en un JFrame. Este objeto es una fuente de eventos que notificará a dos objetos diferentes, receptores de eventos, de eventos de tipo Window.

Uno de los objetos Listener, receptores de eventos, implementa el interfaz WindowListener y define todos los métodos que se declaran en ese interfaz. El otro objeto Listener, extiende la clase Adapter, adaptador, llamada WindowAdapter, que ya no tiene porqué sobreescribir todos los métodos del interfaz, sino solamente aquellos que le resultan interesantes.

La aplicación no termina y devuelve el control al sistema operativo, sino que esto debe forzarse. El código del ejemplo se reproduce a continuación:

import java.awt.*;
import java.awt.event.*;      // Este es el paquete de eventos del JDK 1.1
import java.awt.swing.*;      // Este es el paquete de Swing

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

// Esta clase se utiliza para instaciar un objeto de tipo interfaz de
// usuario, para que permita instanciar a su vez dos objetos Listener
// y registrarlos para que reciban notificacion cuando se producen
// eventos en una Ventana
class IHM {
    // Constructor de la clase
    public IHM() {
        // Se crea un objeto JFrame
        JFrame ventana = new JFrame();

        // El metodo setSize() reemplaza al metodo resize() del JDK 1.0
        ventana.setSize( 300,200 );
        ventana.setTitle( "Tutorial de Java, Eventos" );
        // El metodo setVisible() reemplaza al metodo show() del JDK 1.0
        ventana.setVisible( true );

        // Se instancian dos objetos receptores que procesaran los
        // eventos de la ventana    
        Proceso1 ventanaProceso1 = new Proceso1( ventana );
        Proceso2 ventanaProceso2 = new Proceso2();

        // Se registran los dos objetos receptores para que sean 
        // notificados de los evetnos que genere la ventana, que es el
        // objeto origen de los eventos
        ventana.addWindowListener( ventanaProceso1 );
        ventana.addWindowListener( ventanaProceso2 );
        }
    }

// Las dos clases siguientes se pueden utilizar para instanciar los
// objetos receptor. Esta clase implementa el interfaz WindowListener,
// lo cual requiere que todos los metodos que estan declarados en el
// interfaz sean definidos en la clase. 
// La clase define todos esos metodos y presenta un mensaje
// descriptivo cada vez que se invoca a uno de ellos.
class Proceso1 implements WindowListener {
    // Variable utilizada para guardar una referencia al objeto JFrame
    JFrame ventanaRef;

    // Constructor que guarda la referencia al objeto JFrame
    Proceso1( JFrame vent ){
        this.ventanaRef = vent;
        }

    public void windowClosed( WindowEvent evt ) {
        System.out.println( "Metodo windowClosed de Proceso1" );
        }

    public void windowIconified( WindowEvent evt ) {
        System.out.println( "Metodo windowIconified de Proceso1" );  
        }

    public void windowOpened( WindowEvent evt ) {
        System.out.println( "Metodo windowOpened de Proceso1" );
        }

    public void windowClosing( WindowEvent evt ) {
        System.out.println( "Metodo windowClosing de Proceso1" );
        // Se oculta la ventana
        ventanaRef.setVisible( false ); 
        }

    public void windowDeiconified( WindowEvent evt ) {
        System.out.println( "Metodo windowDeiconified Proceso1" );
        }

    public void windowActivated( WindowEvent evt ) {
        System.out.println( "Metodo windowActivated de Proceso1" );
        }

    public void windowDeactivated( WindowEvent evt ) {
        System.out.println( "Metodo windowDeactivated de Proceso1" );
        }
    }

// Esta clase y la anterior se pueden utilizar para instanciar
// objetos Listener. En esta clase, se extiende la clase Adapter
// obvienado el requerimiento de tener que definir todos los
// metodos del receptor de eventos WindowListener. El objeto
// Adapter, WindowAdapter extiende a WindowListener y define
// todos los metodos con codigo vacio, que pueden ser sobreescritos
// siempre que se desee. En este clase concreta, solamente se 
// sobreescriben dos de los metodos declarados en el interfaz, y
// presenta un mensaje cada vez que se invoca a uno de ellos
class Proceso2 extends WindowAdapter {
    public void windowIconified( WindowEvent evt ) {
        System.out.println( "--- Metodo windowIconified de Proceso2" );
        }

    public void windowDeiconified( WindowEvent evt ) {
        System.out.println( "---Metodo windowDeiconified de Proceso2" );
        }
    }

Lo único destacable del código es el fragmento del comienzo del constructor de la clase IHM en que se utiliza la clase JFrame para instanciar al contenedor principal del interfaz gráfico.

class IHM {
    // Constructor de la clase
    public IHM() {
        // Se crea un objeto JFrame
        JFrame ventana = new JFrame();

        // El metodo setSize() reemplaza al metodo resize() del JDK 1.0
        ventana.setSize( 300,200 );
        ventana.setTitle( "Tutorial de Java, Eventos" );

Si se compila y ejecuta el programa, el lector podrá comprobar que no hay diferencias aparentes con el ejemplo java1101.java, presentado en la sección anterior.

Pero tampoco las cosas son tan sencillas como parecen, porque hay ocasiones en que la conversión de un programa de AWT a Swing no es tan simple como una sustitución e importar la librería. Esto es lo que se muestra en el siguiente ejemplo, java1117.java, que es la contrapartida en Swing del ejemplo java1102.java, ya visto al tratar del AWT. Se ha hecho lo mismo que en el caso anterior, sustituir Frame por JFrame e incorporar la sentencia que importa la librería de Swing.

La intención del programa era presentar las coordenadas del cursor en la posición en que se picase, dentro del área de influencia del Frame, en este caso del JFrame.

import java.awt.*;
import java.awt.event.*;      // Este es el paquete de eventos del JDK 1.1
import java.awt.swing.*;      // Este es el paquete de Swing

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

// Se crea una subclase de JFrame para poder sobreescribir el metodo
// paint(), y presentar en pantalla las coordenadas donde se haya
// producido el click del raton
class MiFrame extends JFrame {
    int ratonX;
    int ratonY;

    public void paint( Graphics g ) {
        g.drawString( ""+ratonX+", "+ratonY,ratonX,ratonY );
        }
    }

// Esta clase se utiliza para instaciar un objeto de tipo interfaz de
// usuario
class IHM {
    public IHM() {
        MiFrame ventana = new MiFrame();

        ventana.setSize( 300,300 );
        ventana.setTitle( "Tutorial de Java, Eventos" );
        ventana.setVisible( true );

        // Se instancia y registra un objeto receptor de eventos
        // para terminar la ejecucion del programa cuando el
        // usuario decida cerrar la ventana
        Proceso1 procesoVentana1 = new Proceso1();
        ventana.addWindowListener( procesoVentana1 );

        // Se instancia y registra un objeto receptor de eventos
        // que sera el encargado de procesas los eventos del raton
        // para determinar y presentar las coordenadas en las que
        // se encuentra el cursor cuando el usuario pulsa el boton
        // del raton
        ProcesoRaton procesoRaton = new ProcesoRaton( ventana );
        ventana.addMouseListener( procesoRaton );
        }
    }

// Esta clase Receptora monitoriza las pulsaciones de los botones
// del raton y presenta las coordenadas en las que se ha producido
// el click
// Se trata de una clase Adpater, luego solo se redefinen los metodos
// que resulten utiles para el objetivo de la aplicacion
class ProcesoRaton extends MouseAdapter {
  MiFrame ventanaRef; // Referencia a la ventana

    // Constructor
    ProcesoRaton( MiFrame ventana ) {
        // Guardamos una referencia a la ventana
        ventanaRef = ventana;
        }

    // Se sobreescribe el metodo mousePressed para determinar y
    // presentar en pantalla las coordenadas del cursor cuando
    // se pulsa el raton
    public void mousePressed( MouseEvent evt ) {
        // Recoge las coordenadas X e Y de la posicion del cursor
        // y las almacena en el objeto JFrame
        ventanaRef.ratonX = evt.getX();
        ventanaRef.ratonY = evt.getY();
        // Finalmente, presenta los valores de las coordenadas
        ventanaRef.repaint();
        }
    }

// Este repector de eventos de la ventana se utiliza para concluir
// la ejecucion del programa cuando el usuario pulsa sobre el boton
// de cierre del JFrame
class Proceso1 extends WindowAdapter {
    public void windowClosing( WindowEvent evt ) {
        System.exit( 0 );
        }
    }

Si se compila y ejecuta el programa, inicialmente todo parece ir bien. Sin embargo, cuando se pican varias veces, se observa que las indicaciones de las coordenadas anteriores no desaparecen de la pantalla. Si se hace que el foco pase a otra de las aplicaciones que estén corriendo, entonces sí desaparecen esos textos antiguos de la ventana. Quizá sea un ejercicio interesante para el lector el descubrir por sí mismo el porqué de este funcionamiento, aparentemente anómalo.

La única parte destacable del código del programa, por indicar algo, es la que extiende la clase JFrame para dar origen a la clase MiFrame, en aras de hacer posible la sobreescritura del método paint() de la clase JFrame.

class MiFrame extends JFrame {
    int ratonX;
    int ratonY;

    public void paint( Graphics g ) {
        g.drawString( ""+ratonX+", "+ratonY,ratonX,ratonY );
        }
    }

El lector recordará que en el caso del ejemplo java1102.java, la clase Frame se extendía de una forma similar para dar origen a otra clase donde poder sobrescribir el método paint().

Nuevos Eventos en Swing

Aunque en Swing la forma de manejar los eventos es similar a la del AWT del Modelos de Delegación, las clases Swing proporcionan una serie de nuevos tipos de eventos, algunos de los cuales se van a presentar en esta sección.

Una de las formas más fáciles de identificar estos nuevos tipos de eventos que incorpora Swing, es consultar los interfaces definidos en Swing o, también, echar una ojeada a la definición de las clases de los eventos en Swing. Ahora se presentan dos tablas, la de la izquierda muestra una lista de los interfaces receptor definidos en el paquete Swing y la de la derecha muestra la lista de clases evento definidas en ese mismo paquete.

AncestorListener
CaretListener
CellEditorListener
ChangeListener
DocumentEvent
DocumentListener
HyperlinkListener
InternalFrameListener
ListDataListener
ListSelectionListener
MenuListener
PopMenuListener
TableColumnModelListener
TableModelListener
TreeExpansionListener
TreeModelListener
TreeSelectionListener
UndoableEditListener
AncestorEvent
CaretEvent
ChangeEvent
EventListenerList
HyperlinkEvent
InternalFrameAdapter
ListDataEvent
ListSelectionEvent
MenuEvent
PopMenuEvent
TableColumnModelEvent
TableModelEvent
TreeExpansionEvent
TreeModelEvent
TreeSelectionEvent
UndoableEditEvent

Se puede observar que no hay una correspondencia obvia entre los interfaces receptores y las clases de evento en todos los casos. En los dos ejemplos que siguen, se verán la clase AncestorEvent y el interfaz AncestorListener.

El primero de estos programas, java1118.java, muestra el uso de getContentPane() para añadir un objeto Swing de tipo JButton a un JFrame, de la misma forma que se ha visto en ejemplos de otras secciones, pero en este caso se profundiza en lo que a los eventos se refiere, ilustrando el uso del interfaz AncestorListener sobre un objeto Swing JButton.

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

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

// Clase para instanciar un objeto de tipo interfaz gráfico
class IHM {
    public IHM(){
        // Crea un JFrame y le pone título, tamaño, etc.
        JFrame ventana = new JFrame();

        ventana.setSize( 300,300 );
        ventana.setTitle( "Tutorial de Java, Swing" );

        // Se añade el receptor de eventos de la ventana para concluir la
        // ejecución del programa
        ventana.addWindowListener( new Conclusion() );

        // Se crea un objeto JButton
        JButton boton = new JButton( "Boton" );

        // Se registra un objeto AncestorListener sobre cada JButton     
        boton.addAncestorListener( new MiAncestorListener() );

        // Se añade el botón al objeto JFrame
        ventana.getContentPane().add( boton );

        System.out.println( "Se hace visible el JFrame" );
        ventana.setVisible( true );    
        }

    // Esta clase se utiliza para concluir el programa cuando el
    // usuario decide cerrar la ventana
    class Conclusion extends WindowAdapter {
        public void windowClosing( WindowEvent evt ) {
            System.exit( 0 );
            }
        }

    // Definicion de la clase AncestorListener
    class MiAncestorListener implements AncestorListener{
        // Se definen los tres métodos declarados en el interfaz
        // AncestorListener     
        public void ancestorAdded( AncestorEvent evt ) {
            System.out.println( "Llamada al metodo ancestorAdded" );
            System.out.println( "Origen Evento: " + evt.getSource() );
            System.out.println( "Ancestor: " + evt.getAncestor() );
            System.out.println( "Padre: " + evt.getAncestorParent() );
            System.out.println( "Componente: " + evt.getComponent() );
            System.out.println( "ID: " + evt.getID() );
            }

        public void ancestorRemoved( AncestorEvent evt ) {
            System.out.println( "Metodo ancestorRemoved" );
            }

        public void ancestorMoved( AncestorEvent evt ) {
            System.out.println( "Metodo ancestorMoved" );
            }
        }
    }

A la hora de repasar el código de la aplicación, en este caso hay que empezar desde las sentencias import, porque ya en ellas está el código que permite que tanto compilador como intérprete Java, puedan acceder a las clases Swing.

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

El método main(), en este caso es tan simple, que no merece comentario alguno. No obstante, ahí se construye un objeto de la clase IHM que será el que se presente en pantalla. El constructor de esta clase, reproducido en las siguiente líneas, es muy sencillo.

class IHM {
    public IHM(){
        // Crea un JFrame y le pone título, tamaño, etc.
        JFrame ventana = new JFrame();

        ventana.setSize( 300,300 );
        ventana.setTitle( "Tutorial de Java, Swing" );

        // Se añade el receptor de eventos de la ventana para concluir la
        // ejecución del programa
        ventana.addWindowListener( new Conclusion() );

        // Se crea un objeto JButton
        JButton boton = new JButton( "Boton" );

        // Se registra un objeto AncestorListener sobre cada JButton     
        boton.addAncestorListener( new MiAncestorListener() );

        // Se añade el botón al objeto JFrame
        ventana.getContentPane().add( boton );

        System.out.println( "Se hace visible el JFrame" );
        ventana.setVisible( true );    
        }

Instancia un objeto Swing de tipo JFrame, fijando su tamaño, proporcionándole un título, etc. Además, también se añade un objeto WindowListener para recoger los eventos de la ventana y terminar el programa cuando el usuario cierre el objeto JFrame.

Luego se instancia un objeto Swing de tipo JButton y se registra un objeto de tipo AncestorListener sobre ese botón. A continuación, se añade el objeto JButton al objeto JFrame ventana, invocando al método getContentPane() y luego al método add() sobre el contenido anterior. Por fin, se presenta un mensaje y se hace visible el objeto JFrame, concluyendo el construtor.

El método getContentPane() no existe en el AWT, donde la única forma de añadir Componentes es manipular directamente el área cliente del objeto Frame. En Swing, sin embargo, algunos paneles son colocados automáticamente sobre el ásrea cliente de un objeto JFrame, con lo cual se pueden añadir Componentes, o manipular, estos paneles en vez de manipular el área cliente del objeto JFrame directamente.

La definición de la clase JFrame es la siguiente:

public class JFrame extends Frame implements WindowConstants, Accesible,RootPaneContainer

Es decir, es una extensión de la clase Frame del AWT que le añade soporte para recibir entradas y pintar sobre objetos Frame hijos, soporte para hijos especiales controlados por un LayeredPanel y soporte para las barras de menú de Swing. Esta clase JFrame es ligeramente incompatible con la clase Frame del AWT. JFrame contiene un objeto JRootPane como único hijo. El contentPane debería ser el padre de cualquier hijo del JFrame. Esto difiere con respecto al Frame del AWT; por ejemplo, para añadir un hijo a un Frame, se escribiría:

frame.add( hijo );

mientras que en el caso del JFrame, es necesario añadir el hijo al contentPane, de la siguiente forma:

frame.getContentPane().add( hijo );

Esto mismo es válido a la hoja de fijar el controlador de posicionamiento de los Componentes, eliminar Componentes, listar los hijos, etc. Todos estos métodos normalmente, deberían ser enviados al contentPane en vez de directamente al JFrame. El contentPane siempre será distinto de nulo y, por defecto, tendrá un BorderLayout como controlador de posicionamiento. Si se intenta hacer que contentPane sea nulo, el sistema generará una excepción. En este ejemplo es suficiente con insertar una llamada al método entre la referencia al objeto JFrame y las llamada a add(), setLayout(), etc. En programas más complejos, las ramificaciones probablemente creasen mayores problemas.

El interfaz de usuario del programa tiene dos clases anidadas. Una de ellas es una clase WindowListener que se utiliza para concluir la ejecución del programa cuando el usuario cierra el JFrame. Es muy simple, y ya se ha visto en otras secciones, aquí se incluye como anidada para mostrar la versatilidad de Java y ver que se puede hacer lo mismo de varias formas diferentes.

La segunda clase anidada se utiliza para instancia un objeto de tipo AncestorListener que será registrado sobre el objeto JButton. Esto ya es un poco más interesante. El interfaz AncestorListener declara tres métodos, así que la clase debe implementar estos tres métodos, que son:

ancestorAdded( AncestorEvent ), llamado cuando el origen o uno de sus antecesores se hace visible, bien porque se llame al método setVisible() pasándole el parámetro true, o porque se haya añadido el Componente a la jerarquía.

ancestorMoved( AncestorEvent ), llamado cuando el origen o uno de sus antecesores es movido.

ancestorRemoved( AncestorEvent ), llamado cuando el origen o uno de sus antecesores se hace invisible, bien porque se llame al método setVisible() pasándole el parámetro false, o porque se haya eliminado el Componente de la jerarquía.

Como se puede observar, cuando alguno de estos método es llamado, se le pasa un objeto de tipo AncestorEvent como parámetro. Cuando se llama al primero de ellos, invoca a su vez a métodos del AncestorEvent que se le pasa para presentar en pantalla información sobre el antecesor.

class MiAncestorListener implements AncestorListener{
    // Se definen los tres métodos declarados en el interfaz
    // AncestorListener     
    public void ancestorAdded( AncestorEvent evt ) {
        System.out.println( "Llamada al metodo ancestorAdded" );
        System.out.println( "Origen Evento: " + evt.getSource() );
        System.out.println( "Ancestor: " + evt.getAncestor() );
        System.out.println( "Padre: " + evt.getAncestorParent() );
        System.out.println( "Componente: " + evt.getComponent() );
        System.out.println( "ID: " + evt.getID() );
        }

La verdad es que si se compila y ejecuta el programa, se obtendría algo como la salida de pantalla capturada a continuación:

% java java1118
Se hace visible el JFrame
Llamada al metodo ancestorAdded
Origen Evento: java.awt.swing.JButton[,0,0,0x0,invalid,
    layout=java.awt.swing.OverlayLayout]
Ancestor: java.awt.swing.JButton[,0,0,0x0,invalid,
    layout=java.awt.swing.OverlayLayout]
Padre: java.awt.swing.JPanel[null.contentPane,0,0,0x0,invalid,
    layout=java.awt.swing.JRootPane$1]
Componente: java.awt.swing.JButton[,0,0,0x0,invalid,
    layout=java.awt.swing.OverlayLayout]
ID: 1
Metodo ancestorMoved

y esto no parece coincidir demasiado con las descripciones que JavaSoft proporciona en su documentación sobre los métodos getAncestor() y getAncestorParent(). La salida parece referirse al objeto JButton como antecesor y al objeto JRootPane como padre del antecesor. Pero lo cierto es que el objeto JButton es un hijo, no un antecesor; aunque es de imaginar que esto dependerá de la interpretación que se le quiera dar. El caso es que no está demasiado claro en la documentación.

El fragmento final del código es el que muestra la definición de los otros dos métodos del interfaz AncestorListener, y que se reproduce a continuación:

public void ancestorRemoved( AncestorEvent evt ) {
    System.out.println( "Metodo ancestorRemoved" );
    }

public void ancestorMoved( AncestorEvent evt ) {
    System.out.println( "Metodo ancestorMoved" );
    }

Si se ejecuta el programa, aparte de observar la salida referida anteriormente, se observa que cuando se mueve, iconiza o desiconiza la ventana, se generan llamadas al método ancestorMoved(). Cuando se hace visible el JFrame, se llaman a los dos métodos, ancestorAdded() y ancestorMoved().

A continuación se muestra otro ejemplo, java1119.java, que ilustra el uso de un receptor de tipo AncestorListener sobre un JButton, y lo que es más importante, ilustra el hecho de que objetos como JButton pueden ser contenedores de otros objetos, incluyendo entre ellos a otros objeto JButton.

El programa apila tres objetos JButton apilados y colocados sobre un objeto JFrame, tal como muestra la figura anterior. Los objetos ActionListener se registran sobre cada uno de los botones para atrapar los eventos de tipo Action cuando se pulsa el botón y poder presentar la información correspondiente al origen del evento. Los objetos AncestorListener también son registrados sobre los objetos JButton.

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

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

// Clase para instanciar un objeto de tipo interfaz gráfico
class IHM {
    public IHM() {
        // Crea un JFrame y le pone título, tamaño, etc.
        JFrame ventana = new JFrame();

        ventana.setSize( 300,100 );
        ventana.setTitle( "Tutorial de Java, Swing" );

        // Obsérvese la utilización de getContentPane() en la siguiente 
        // sentencia
        ventana.getContentPane().setLayout( new FlowLayout() );

        // Se añade el receptor de eventos de la ventana para concluir la
        // ejecución del programa
        ventana.addWindowListener( new Conclusion() );

        // Se crean los tres objetos JButton     
        JButton primerBoton = new JButton( "Primer Boton" );
        JButton segundoBoton = new JButton( "Segundo Boton" );
        JButton tercerBoton = new JButton( "Tercer Boton" );

        // Se apilan los botones uno sobre otro     
        primerBoton.add( segundoBoton );
        segundoBoton.add( tercerBoton );

        // Se registra un objeto AncestorListener sobre cada JButton     
        primerBoton.addAncestorListener( new MiAncestorListener() );
        segundoBoton.addAncestorListener( new MiAncestorListener() );
        tercerBoton.addAncestorListener( new MiAncestorListener() );

        // Se registra un objeto ActionListener sobre cada JButton     
        primerBoton.addActionListener( new MiActionListener() );
        segundoBoton.addActionListener( new MiActionListener() );
        tercerBoton.addActionListener( new MiActionListener() );

        // Se añade el primer botón, que contiene a los demás, al
        // objeto JFrame
        ventana.getContentPane().add( primerBoton );

        System.out.println( "Se hace visible el JFrame" );
        ventana.setVisible( true );    
        }

    // Esta clase se utiliza para concluir el programa cuando el
    // usuario decide cerrar la ventana
    class Conclusion extends WindowAdapter{
        public void windowClosing( WindowEvent evt ) {
            System.exit( 0 );
            }
        }

    // Definicion de la clase AncestorListener
    class MiAncestorListener implements AncestorListener {
        // Se definen los tres métodos declarados en el interfaz
        // AncestorListener, incorporando el moldeo necesario para
        // que nadie se queje
        public void ancestorAdded( AncestorEvent evt ) {
            System.out.println( "Metodo ancestorAdded" );
            System.out.println( " Origen del evento: " + 
                ( (JButton)evt.getSource() ).getActionCommand() );
            }

        public void ancestorRemoved( AncestorEvent evt ) {
            System.out.println( "Metodo ancestorRemoved" );
            System.out.println( " Origen del evento: " + 
                ( (JButton)evt.getSource() ).getActionCommand() );
            }

        public void ancestorMoved( AncestorEvent evt ) {
            System.out.println( "Metodo ancestorMoved" );
            System.out.println( " Origen del evento: " + 
                ( (JButton)evt.getSource() ).getActionCommand() );
            }
        }

    // Definicion de la clase ActionListener
    class MiActionListener implements ActionListener {
        public void actionPerformed( ActionEvent evt ) {
            System.out.println( "Metodo actionPerformed" );
            System.out.println( " Origen del evento: " + 
                ( (JButton)evt.getSource() ).getActionCommand() );
            }
        }
    }

La mayor parte del código de este programa es semejante al de los anteriores. Cuando se instancian los tres botones, se apilan añadiendo el segundoBoton al primerBoton y añadiendo el tercerBoton al segundoBoton.

Se registra un AncestorListener sobre los tres botones y luego un objeto ActionListener también sobre ellos. La clase AncestorListener es muy similar a la del ejemplo anterior; no obstante, es de notar la necesidad de moldeo en esta versión del método. Esto se debe a la invocación del método getSource() que devuelve un objeto de tipo Object, que debe ser moldeado a un JButton para que pueda ser utilizado.

class MiAncestorListener implements AncestorListener {
    // Se definen los tres métodos declarados en el interfaz
    // AncestorListener, incorporando el moldeo necesario para
    // que nadie se queje
    public void ancestorAdded( AncestorEvent evt ) {
        System.out.println( "Metodo ancestorAdded" );
        System.out.println( " Origen del evento: " + 
            ( (JButton)evt.getSource() ).getActionCommand() );
        }

Y ya, lo más interesante es ver cómo la clase ActionListener atrapa los eventos de tipo Action que se producen en los botones cuando se pulsan.

class MiActionListener implements ActionListener {
    public void actionPerformed( ActionEvent evt ) {
        System.out.println( "Metodo actionPerformed" );
        System.out.println( " Origen del evento: " + 
            ( (JButton)evt.getSource() ).getActionCommand() );
        }
    }

Navegador

Home | Anterior | Siguiente | Indice | Correo