sábado, 28 de julio de 2007

Interfaces y Listeners en Java ;-)

¡Por fin! Esto no fue un quebradero de cabeza como los que me llevaba con el NekroEditor, lo que ocurre es que como ando saliendo de la novata del ámbito de Java aún me encuentro con conocimientos nuevos. Esta vez tuve que comprender bien cómo era eso de las clases abstractas, tuve que aprender sobre las interfaces que no es nada difícil pero tuve que arrancar de la nada y comprenderlo todo.

Lo que intenté solucionar fue el por qué mi programita de una ventana con un botón no cerraba cuando le hacía click en la tachita, lo que me costó googlear y leer durante varias horas en foros y manuales que tengo en del.icio.us. No voy a relatar toda la historia porque son tantos los lugares que vi, tantas las conclusiones que saqué que ya hasta se me olvidaron; jajaja bueno, será que se me olvidaron los sitios y el orden pero los conocimientos ya los tengo.

Una de las cosas que hice fue examinar un código que había hecho y que venía en un tutorial de Java que descargué hace años, el primero que tuve y que imprimí para leerlo en papel que fue el que me dio la introducción clave. Resulta que fue el primero al que se me ocurrió recurrir cuando me inicié verdaderamente en esto. El caso es que ahí venía un sencillo ejercicio de crear un Applet con unos cuantos elementos. Lo ejecuté y vi que la ventanita se cerraba, el código era este:


public void processEvent(AWTEvent mensaje){

        if (mensaje.getID() == Event.WINDOW_DESTROY){
              if (inAnApplet){
                     dispose();
              }
              else{
                     System.exit(0);
              }
       }

/* return super.processEvent(mensaje); */

}


Me pareció algo tonto y decidí estudiar la clase directamente de la página de la API de Sun, bueno, entré e intenté buscarla pero no apareció por el índice, no sé ni cómo di con ella pero me di cuenta que era otra de las funciones que había dejado el Java Team obsoletas. No sé si lo que hicieron con SDK 6 fue un gran salto o volver a reprogramar el lenguaje de programación porque hay más "deprecated functions/methods/fields" que cosas nuevas :-S Es por ello que me resultó muy complicado seguir con los tutoriales que encuentro en internet como el que imprimí que es de hace casi 10 años creo y me tuve que arrancar a la intuición y las googleadas partiendo de la página de la API de sun, que tuve que hacer mi mejor amiga (Google ya lo era, de hecho es mi tío).

Estaba deprecada la función, lo demás fue tirada de rollo mías. Me dijo algo que ya había leído antes: "usa processWindowEvent(WindowEvent e) si quieres ser alguien en el futuro" puesto que, aunque aún servía la clase Event, eso de usar cosas por compatibilidad con lo viejo no es lo mío. Ni modo, a aprender a usar los eventos.

Y ese compromiso fue muy difícil ya que usar processWindowEvent va también de saber usar las interfaces debido a que, para que este método sirva de algo hay que añadir donde sea que se pueda ejecutar un addWindowListener que es como un "agregarEscuchadordeEventos".

Java ya tiene uno por defecto, al igual que cada Sistema Operativo en su API, el Gestor de Eventos que se encarga de procesar los mensajes de los controles en general. Pero hay un problema con esto: las aplicaciones de Java pueden ejecutarse de dos formas: como Applets y como aplicaciones Standalone, los Applets se pueden visualizar acuñándolos en el código HTML de una págia y verlos en el navegador o con el appletviewer que incluye Java, y las Standalone desde la consola, son como los programas básicos en C que usan stdio.h. Si estamos en modo Standalone matamos una aplicación con System.exit(0); y si estamos en un applet usamos dispose(); pero Java no tiene forma de saber si la aplicación es arrancada desde una consola o desde el navegador puesto que lo único que hay es un bytecode .class, la forma en que lo queramos ejecutar es otra cosa, entonces no tiene forma de saber cómo acabar una aplicación. Y en la misma página de Sun terminé de disipar toda duda que pudiera tener (Clase Frame):

Frames are capable of generating the following types of WindowEvents:


  • WINDOW_OPENED

  • WINDOW_CLOSING:
    If the program doesn't explicitly hide or dispose the window while processing this event, the window close operation is canceled.

  • WINDOW_CLOSED

  • WINDOW_ICONIFIED

  • WINDOW_DEICONIFIED

  • WINDOW_ACTIVATED

  • WINDOW_DEACTIVATED

  • WINDOW_GAINED_FOCUS

  • WINDOW_LOST_FOCUS

  • WINDOW_STATE_CHANGED




Justamente el mensaje que necesitaba se ve bloqueado y no me queda más de otra que agregar un WindowListener. WindowListener es una interfase, y yo apenas sabía los conceptos básicos de las interfaces, en fin que estuve leyendo un par de horas ya algo hastiado pero como mi curiosidad era más grande no me quedó de otra que aguantarme, y es que cuando le llega a uno el "¿y si la solución está en el siguiente?" remuerde quedarse con la duda de si el próximo resultado, el próximo párrafo o el próximo intento serán los correctos.

Implementar una interfase fue lo más fácil, puesto que sólo tenía que declarar los métodos y ponerlos en blanco: {} salvo el que necesitaba, donde había que poner un System.exit(0);.

Pero no tenía idea de cómo usar el addWindowListener(WindowListener l), pues el tipo de datos que requería el parámetro era una interfaz y no sabía dónde tenía que implementarla: buscando códigos de ejemplo vi dos que me resultaron muy raros y que hasta ahora no entiendo del todo bien:

public static void main(String s[]) {
   JFrame frame = new JFrame("A Basic Frame");

   WindowListener l = new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
         System.exit(0);
      }
   };
   frame.addWindowListener(l);
   //BLA BLA BLA
}


Que es de esta página: Cómo Crear Frames (Ventanas Principales). Luego otro de elhacker.net:

...bueno es este: HAKIIIII!!!11eleven Es que está muy largo.


Pero no son el objeto de mi explicación estos códigos que no entiendo bien cómo usan la interfase.

Perp ya pude solucionarlo: creo una clase que implemente la interfase WindowListener, luego creo una instancia de esa clase y se lo paso a addWindowListener. Sencillo pero para mi mente poco adentrada en Java le costó algo de trabajo asimilarlo, y me siento muy orgulloso de haber logrado esto, aprendí varias cosas el día de hoy y pude solucionar un problema y prever más.

El código resultante el siguiente, el cuál es el que llevo hasta ahora, pero le seguiré modificando muchas cosas porque, como ya dije antes, sigo con el problema de que mi botón de comando se redimensiona apenas encuentra un pretexto:

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

public class Prueva extends Frame{

       public static void main(String args[]){

              Prueva app = new Prueva("Hols!");

       }

       public Prueva(String title){

              super(title);

              wndListener Eventos = new wndListener();
              addWindowListener(Eventos);


              /*Button button = new Button("Heisann");
              add(button);
              button.setBackground(Color.MAGENTA);
              button.setLocation(64, 64);
              button.setSize(64, 64);*/

              setBackground(Color.CYAN);
              setSize(256, 256);
              setVisible(true);

       }

       protected void processWindowEvent(WindowEvent e){

              super.processWindowEvent(e);

       }

}


class wndListener implements WindowListener{

       public void windowOpened (WindowEvent e){}

       public void windowClosing (WindowEvent e){

              System.exit(0);

       }


       public void windowClosed (WindowEvent e){}

       public void windowIconified (WindowEvent e){}
       public void windowDeiconified (WindowEvent e){}

       public void windowActivated (WindowEvent e){}
       public void windowDeactivated (WindowEvent e){}

}


Hilsener.

2 comentarios:

  1. Anónimo7:59 p.m.

    muy buena aportacion sigue asi

    ResponderBorrar
  2. Jolines.. con cuanta pasion lo escribes.. ni yo ke trabajo con java me emociono tanto.. weno.. pos na.. no se en ke momento escribiste esto.. solo ke me ha llamado la atención por ke como tu estaba buscando informacion sobre el metodo dispose() grax por dejarmelo mas claro.. en fin.. opino lo mismo del anterior anónimo sigue asi!!!

    ResponderBorrar