Bases del nuevo modelo

prev.gif (997 bytes)chapter.gif (1105 bytes)home.gif (1232 bytes)next.gif (1211 bytes)

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

Cómo genera JBuider el código

Clases internas

Clases internas anónimas

Separando el código de inicialización de la respuesta.


Sucesos, componentes, interfaces

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.

  1. Una clase que implemente un interface, por ejemplo ActionListener
class MiClase implements ActionListener{
	//...
}
  1. La implementación del método del interface
public void actionPerformed(ActionEvent ev){
    //código respuesta a la acción del usuario sobre el componente
}
  1. La relación entre la fuente de los sucesos (el componente o componentes) y el objetodeMiClase que está interesado o que maneja el suceso.
componente.addActionListener(objetoDeMiClase);
  1. La información acerca del suceso viene encapsulada en un objeto de una clase específica derivada de Event.

 

Respuesta a la acción de pulsar sobre un botón

Veamos un ejemplo sencillo que consiste en un botón que al ser pulsado muestra un mensaje en un control etiqueta (label).

El control Button

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");

El control Label

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");

Propósito

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"

Diseño

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.

Respuesta a las acciones del usuario

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.

 

La clase que describe el applet implementa el interface Listener

disco.gif (1035 bytes)boton1: BotonApplet1.java

El código consta como hemos descrito en el primer apartado, de tres partes

  1. La clase que describe el applet BotonApplet1 implementa el interface ActionListener y define la función actionPerformed
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.

  1. El control btnAceptar es la fuente de los sucesos (events), y el objeto (listener) que los maneja es el objeto applet o this. La función que asocia ambos objetos se denomina addActionListener. En la función miembro init, el control botón llama a addActionListener y le pasa el objeto (el applet) this que maneja los sucesos.
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");
  }
}

 

Una clase denominada AccionBoton implementa el interface Listener

disco.gif (1035 bytes)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");
 }
}

 

Cómo genera JBuilder el código (Standard adapter)

disco.gif (1035 bytes)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.

code_style.gif (7998 bytes)

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

action_performed.gif (29077 bytes)

 

Clases internas

disco.gif (1035 bytes)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.

 

Clases internas anónimas

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(){//...});

 

Separando el código de inicialización de la respuesta (Anonymous adapter)

disco.gif (1035 bytes)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.