Sucesos (events) |
Sucesos, componentes, interfaces
Respuesta a la acción de pulsar sobre un botón
La clase que describe el applet implementa el interface Listener
Una clase denominada AccionBoton implementa el interface Listener
Separando el código de inicialización de la respuesta.
En el modelo AWT 1.1 los sucesos (events) son generados por fuentes (sources). Uno o más objetos interesados (listeners) son notificados que tal o cual suceso se ha producido en una fuente particular.
Los objetos interesados en los sucesos, también llamados manejadores de sucesos (event handlers) pueden ser objetos de cualquier clase, siempre que dicha clase implemente un determinado interface. Son necesarios tres pasos para gestionar los sucesos.
class MiClase implements ActionListener{ //... }
public void actionPerformed(ActionEvent ev){ //código respuesta a la acción del usuario sobre el componente }
componente.addActionListener(objetoDeMiClase);
Veamos un ejemplo sencillo que consiste en un botón que al ser pulsado muestra un mensaje en un control etiqueta (label).
El botón o control Button es uno de más simples y utilizados en un interfaz gráfico de usuario.
Button btnAceptar=new Button();
Para establecer el título del botón se llama a setLabel
btnAceptar.setLabel("Aceptar");
La etiqueta o control label sirve para mostrar un mensaje que habitualmente no cambia. Normalmente, acompaña a los controles de edición, para indicar al usuario el tipo de información que tiene que introducir. Para establecer el texto en la etiqueta se llama a setText
lMensaje.setText("Se ha pulsado el botón");
Un control etiqueta muestra el texto "Pulsar el botón". Al pulsar en el botón Aceptar se cambia el texto que aparece en dicho control a "Se ha pulsado el botón"
Crear un applet y situar sobre el applet en el modo diseño (pestaña Design) un control etiqueta (Label) y un botón (Button).
Cambiar sus propiedades en sus respectivas hojas de propiedades
Establecer FlowLayout como gestor de diseño del applet, separando horizontalmente los controles, cambiando la propiedad hgap.
Para responder a la acción de pulsar un botón, hay que implementar el interface ActionListener. Dicho interface tiene una única función miembro denominada actionPerformed. La clase que implementa este interface obligatoriamente ha de definir esta función.
public interface ActionListener extends EventListener { public void actionPerformed(ActionEvent e); }
A lo largo de esta página, vamos a describir las distintas aproximaciones que existen
para responder a las acción sobre el botón, que nos servirán de modelo para los otros
controles.
boton1:
BotonApplet1.java
El código consta como hemos descrito en el primer apartado, de tres partes
public class BotonApplet1 extends Applet implements ActionListener{ Label lMensaje = new Label(); Button btnAceptar = new Button(); //... public void actionPerformed(ActionEvent ev){ lMensaje.setText("Se ha pulsado el botón"); } }En esta función definimos lo que se quiere hacer cuando se pulsa el botón. En este caso, al pulsar el botón btnAceptar se muestra un mensaje en un control etiqueta lMensaje.
public class BotonApplet1 extends Applet implements ActionListener{ Label lMensaje = new Label(); Button btnAceptar = new Button(); public void init(){ //... btnAceptar.addActionListener(this); } //... }De este modo, cada vez que se pulsa el botón se llama a la función respuesta actionPerformed, donde se programan las tareas específicas que deseamos realizar cuando se pulsa el botón.
El código completo de este ejemplo, es el siguiente
package boton1; import java.awt.*; import java.awt.event.*; import java.applet.*; public class BotonApplet1 extends Applet implements ActionListener{ Label lMensaje = new Label(); Button btnAceptar = new Button(); FlowLayout flowLayout1 = new FlowLayout(); public void init(){ lMensaje.setText("Pulsar el botón "); btnAceptar.setLabel("Aceptar"); btnAceptar.addActionListener(this); flowLayout1.setHgap(25); this.setLayout(flowLayout1); this.add(lMensaje, null); this.add(btnAceptar, null); } public void actionPerformed(ActionEvent ev){ lMensaje.setText("Se ha pulsado el botón"); } } |
boton2:
BotonApplet2.java
Vamos a ver una segunda aproximación. Definimos una clase denominada AccionBoton que implementa el interface ActionListener. En init o jbInit (si se genera el código con JBuilder) se asocia el control que es la fuente de los sucesos con un objeto (listener) de dicha clase que maneja los sucesos.
Se define una clase AccionBoton que implementa el interface ActionListener y define la función actionPerformed
class AccionBoton implements ActionListener{ private Label label; public AccionBoton(Label label){ this.label=label; } public void actionPerformed(ActionEvent ev){ label.setText("Se ha pulsado el botón"); } }
Se asocia la fuente que produce los sucesos, el botón btnAceptar, con el objeto accion de la clase AccionBoton que maneja o que está interesado en dichos sucesos.
AccionBoton accion=new AccionBoton(lMensaje); btnAceptar.addActionListener(accion);
La clase AccionBoton es independiente de la clase que describe el applet, BotonApplet2. La función miembro actionPerformed de la clase AccionBoton necesita tener acceso al control lMensaje para que muestre un texto en dicho control cuando se pulsa el botón. Para ello, ponemos como miembro dato de dicha clase un control etiqueta (Label) que inicializamos en el constructor al pasarle lMensaje.
El código completo de este ejemplo, es el siguiente
package boton2; import java.awt.*; import java.awt.event.*; import java.applet.*; public class BotonApplet2 extends Applet{ Label lMensaje = new Label(); Button btnAceptar = new Button(); FlowLayout flowLayout1 = new FlowLayout(); public void init() { lMensaje.setText("Pulsar el botón "); btnAceptar.setLabel("Aceptar"); AccionBoton accion=new AccionBoton(lMensaje); btnAceptar.addActionListener(accion); flowLayout1.setHgap(25); this.setLayout(flowLayout1); this.add(lMensaje, null); this.add(btnAceptar, null); } } //********************************* class AccionBoton implements ActionListener{ private Label label; public AccionBoton(Label label){ this.label=label; } public void actionPerformed(ActionEvent ev){ label.setText("Se ha pulsado el botón"); } } |
boton3:
BotonApplet3.java
Para tener acceso desde la clase AccionBoton a los miembros datos que no sean privados (private) de la clase que describe el applet, en vez de pasarle en el constructor una lista de dichos miembros, le podemos pasar el objeto this que hace referencia el applet. El miembro dato applet de la clase AccionBoton, se inicializa con this en el constructor. Desde el objeto applet se accede al miembro dato lMensaje para mostrar un texto en dicho control. La clase AccionBoton quedaría como sigue.
class AccionBoton implements ActionListener{ private BotonApplet3 applet; public AccionBoton(BotonApplet3 applet){ this.applet=applet; } public void actionPerformed(ActionEvent ev){ applet.lMensaje.setText("Se ha pulsado el botón"); } }
En init creamos un objeto de la clase AccionBoton y le pasamos el applet (this). Luego, asociamos la fuente que produce los sucesos, el botón btnAceptar, con el objeto accion de la clase AccionBoton que los maneja.
public void init(){ //... AccionBoton accion=new AccionBoton(this); btnAceptar.addActionListener(accion); }
JBuilder tiene la posibilidad de generar el código de la declaración de la función respuesta, en el modo diseño, seleccionando el control y eligiendo el panel Events la función respuesta (actionPerformed) que queremos definir. El código generado tiene una forma similar a la siguiente.
package boton3; import java.awt.*; import java.awt.event.*; import java.applet.*; public class BotonApplet3 extends Applet{ Label lMensaje = new Label(); Button btnAceptar = new Button(); FlowLayout flowLayout1 = new FlowLayout(); public void init() { lMensaje.setText("Pulsar el botón "); btnAceptar.setLabel("Aceptar"); AccionBoton accion=new AccionBoton(this); btnAceptar.addActionListener(accion); flowLayout1.setHgap(25); this.setLayout(flowLayout1); this.add(lMensaje, null); this.add(btnAceptar, null); } void btnAceptar_actionPerformed(ActionEvent ev) { lMensaje.setText("Se ha pulsado el botón"); } } //********************************* class AccionBoton implements ActionListener{ private BotonApplet3 applet; public AccionBoton(BotonApplet3 applet){ this.applet=applet; } public void actionPerformed(ActionEvent ev){ applet.btnAceptar_actionPerformed(ev); } } |
Vemos que en la clase que describe el applet BotonApplet3, se define una función respuesta btnAceptar_actionPerformed. Desde la función miembro actionPerformed de la clase AccionBoton se llama a dicha función. De este modo al ser btnAceptar_actionPerformed miembro de la clase que describe el applet tiene acceso a todos sus miembros sean públicos o privados.
Otra ventaja que presenta esta aproximación es que en la clase que define al applet, BotonApplet3, tenemos por una parte el código de inicialización del applet, en init ( jbInit), y por otra el código de las funciones respuesta. El mantenimiento del código es más fácil con esta aproximación, ya que una sola clase controla la inicialización y las funciones respuesta a las acciones del usuario, en vez de estar en clases separadas.
Para que JBuilder genere el código de esta forma es necesario seleccionar en el elemento del menú Run/Parameters. En el cuadro de diálogo que aparece se selecciona la pestaña Code style. Se activa el botón del radio titulado Standard adapter del grupo titulado Event Handling.
Se selecciona el control en modo de diseño, y se hace doble-clic en el panel Events, en el editor asociado al nombre de la función actionPerformed tal como se ve en la figura. En dicho editor podemos cambiar el nombre de la función respuesta btnAceptar_actionPerformed generado por JBuilder. Podemos también generar dicho código haciendo doble-clic sobre el propio botón en el modo diseño
boton4:
BotonApplet4.java
En esta aproximación vamos a mover la clase AccionBoton al interior de la clase que describe el applet. La clase AccionBoton se denomina ahora interna. Una clase interna tiene acceso a los miembros de la clase que la encierra. El uso de las clases internas como veremos simplifica mucho el código. Para mover la clase AccionBoton al interior de la clase que describe el applet simplemente trasladamos la llave de cierre del applet después de la definición de la clase AccionBoton.
package boton4; import java.awt.*; import java.awt.event.*; import java.applet.*; public class BotonApplet4 extends Applet{ Label lMensaje = new Label(); Button btnAceptar = new Button(); FlowLayout flowLayout1 = new FlowLayout(); public void init() { lMensaje.setText("Pulsar el botón "); btnAceptar.setLabel("Aceptar"); btnAceptar.addActionListener(new AccionBoton()); flowLayout1.setHgap(25); this.setLayout(flowLayout1); this.add(lMensaje, null); this.add(btnAceptar, null); } class AccionBoton implements ActionListener{ public void actionPerformed(ActionEvent ev){ lMensaje.setText("Se ha pulsado el botón"); } } } |
Como vemos en el código, la clase interna AccionBoton tiene acceso al miembro dato lMensaje de la clase que describe el applet.
Las clase internas anónimas nos permiten simplificar aún más el código que define la respuesta a la acción del usuario sobre un control. En init o en jbInit (si el código es generado por JBuider) se escribe
public void init(){ //... btnAceptar.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ev){ lMensaje.setText("Se ha pulsado el botón"); } }); }
En unas pocas líneas de código, se crea con new un objeto de una clase anónima que implementa el interface ActionListener
new ActionListener(){ //... }
Dentro de dicha clase se ha de definir la función actionPerformed
new ActionListener(){ public void actionPerformed(ActionEvent ev){ //definir la función respuesta } }
Se asocia mediante addActionListener el control botón btnAceptar con el objeto de la clase anónima que maneja las acciones del usuario sobre dicho botón.
btnAceptar.addActionListener(new ActionListener(){//...});
boton5:
BotonApplet5.java
Para una mejor lectura del código, es mejor separar el código de inicialización del applet, la definición de init (o jbInit), del código que describe la respuesta a las acciones del usuario sobre los controles. Sin modificar la funcionalidad del applet mejoramos la legibilidad del código tal como se muestra en las líneas marcadas en letra negrita.
package boton5; import java.awt.*; import java.awt.event.*; import java.applet.*; public class BotonApplet5 extends Applet{ Label lMensaje = new Label(); Button btnAceptar = new Button(); FlowLayout flowLayout1 = new FlowLayout(); public void init() { lMensaje.setText("Pulsar el botón "); btnAceptar.setLabel("Aceptar"); btnAceptar.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ btnAceptar_actionPerformed(e); } }); flowLayout1.setHgap(25); this.setLayout(flowLayout1); this.add(lMensaje, null); this.add(btnAceptar, null); } void btnAceptar_actionPerformed(ActionEvent ev) { lMensaje.setText("Se ha pulsado el botón"); } } |
Esta es la otra forma que compilador JBuilder genera el código de la respuesta a las acción de pulsar sobre el botón, cuando se selecciona el control en modo de diseño, y se hace doble-clic en el panel Events, en el editor asociado al nombre de la función actionPerformed tal como se ve en la figura más arriba. En dicho editor podemos cambiar el nombre de la función respuesta btnAceptar_actionPerformed generado por JBuilder. Podemos también generar dicho código haciendo doble-clic sobre el propio botón en el modo diseño.
JBuilder genera este código por defecto. Podemos comprobarlo, al seleccionar en el elemento del menú Run/Parameters. En el diálogo Properties que vimos anteriormente, se selecciona la pestaña Code style, veremos activado el botón del radio titulado Anonymous adapter del grupo titulado Event Handling, si no lo hemos cambiado previamente.