Java UI Manejo de eventos
En Java los eventos son representados por objetos Ejemplos: clic en un botn arrastrar el mouse pulsar Enter
Los componentes AWT y Swing generan (fire)
eventos [Link]
Los XXXEvent nos informan...
Quin lo dispara? De qu tipo es? Cundo ocurri? Informacin propia del evento Los detalles del evento pueden ser obtenidos
usando mtodos de acceso:
Ej.: getActionCommand() getModifiers()
Eventos AWT
De bajo nivel Componentes ComponentEvent, FocusEvent, KeyEvent, MouseEvent PaintEvent Contenedores ContainerEvent Ventanas WindowEvent Semnticos mayor nivel de abstraccin ActionEvent, ItemEvent, TextEvent, AdjustmentEvent
Eventos AWT
Eventos semnticos
No son disparados por todos los componentes Ejemplo 1: ItemEvent indica que un tem fue
seleccionado o deseleccionado
Disparado por JComboBox No disparado por JButton Ejemplo 2: ActionEvent Disparado por JComboBox Disparado por JButton
Eventos Swing
Swing tiene adems su propio paquete de manejo de eventos: [Link]
Casi todos estos nuevos eventos estn relacionados con
la arquitectura MVC Ejemplo: TreeModelEvent ListDataEvent
Manejo de eventos
Modelo de Delegacin de Eventos Interfaces listeners
Registro para recibir eventos de una fuente
Patrn Observer
Manejo de eventos
addActionListener(ActionListener l)
Event Sources
Event Listeners
Button
Event
Listeners
Interfaces que manejan los eventos
([Link]). Basadas en Observer Cada clase Event tiene su correspondiente
interface Listener
Varios Listeners para el mismo tipo de eventos
ActionEvent MouseEvent
ActionListener
MouseListener
MouseMotionListener
Listeners
Ejemplos de Listeners:
public interface ActionListener { public void actionPerformed(ActionEvent e); } public interface ItemListener { public void itemStateChanged(ItemEvent e); } public interface ComponentListener { public void componentHidden(ComponentEvent e); public void componentMoved(ComponentEvent e); public void componentResized(ComponentEvent e); public void componentShown(ComponentEvent e); }
public interface WindowListener extends EventListerner { void windowActivated(WindowEvent e); void windowClosed(WindowEvent e); void windowClosing(WindowEvent e);
void windowDeactivated(WindowEvent e);
void windowDeiconified(WindowEvent e); void windowIconified(WindowEvent e); void windowOpened(WindowEvent e);} public interface MouseListener extends EventListener { public void mouseClicked(MouseEvent e);
public void mousePressed(MouseEvent e);
public void mouseReleased(MouseEvent e); public void mouseEntered(MouseEvent e); public void mouseExited(MouseEvent e);}
Registro de Listeners
Un Listener debe en primer lugar registrarse con
la(s) fuente(s) que pueda(n) generar los eventos
de su inters: public void addXXXListener(XXXListener e)
addActionListener, addItemListener, etc.
Para el mismo evento en un componente, puede haber registrado varios Listeners Un evento puede provocar numerosas respuestas Los eventos son difundidos a todos sus Listeners
Mltiples Listeners
Component
addEventListener Ocurre evento
Event
EventListener
addEventListener Ocurre evento Event
EventListener
Conectar un Listener con una fuente de eventos
Defina una clase que implemente la interface Listener(o que
extienda una clase que la implemente)
public class AppFrame extends JFrame implements ActionListener {
Aada la implementacin de la interface
public void actionPerformed(ActionEvent e) { // heres where I do stuff when the action happens
Registre el Listener con la fuente
Jbutton okButton = new JButton(OK); [Link](this);
Tips
Debe implementar todos los mtodos de la interface Si el cdigo usado para implementar el manejo de eventos tiene unas pocas lneas se suele usar una clase interna annima. No hay garanta de cul Listener es notificado primero. No escribir cdigo contando con un orden especfico. Trate eventos semnticos antes que de bajo nivel cambios en look and feel componentes compuestos Utilice mtodos descriptivos de los eventos ActionEvent getActionCommand() Threads
Clases Adapter
Son clases que implementan una interface Listener
con mtodos vacos (dummy), uso herencia. Desventaja: Java no permite herencia mltiple
Solucin: usar clases internas annimas
Utiles slo para interfaces Listeners con ms de un mtodo Principalmente por razones de conveniencia Ejemplos: MouseAdapter
WindowAdapter
Clases internas
En Java una clase puede ser definida en cualquier lugar Anidada dentro de otras clases
En la invocacin de un mtodo
Tienen acceso a los miembros y mtodos de todas las clases externas a ellas
Pueden tener nombres o ser annimas
Pueden extender de otra clase o implementar interfaces Muy tiles para el manejo de eventos
Clases internas con nombre
Se definen como las clases normales No pueden ser public
public class ApplicationFrame { . class ButtonHandler implements ActionListener { Public void actionPerformed(ActionEvent e) { doTheOKThing(); } } private void doTheOKThing() { // heres where I handle OK } . JButton okButton = new JButton(OK); [Link](new ButtonHandler()); // create inner class listener .
Clases internas con nombre
public class MyClass extends JPanel {
[Link](new MyAdapter()); class myAdapter extends MouseAdapter { public void mouseClicked(MouseEvent e) { // blah } // end mouseClicked } // end inner class } // end MyClass
Clases internas annimas
Definida dentro de addXXXListener (new className( ) { classBody }); (new interfaceName() { classBody }); Dentro del cuerpo no puedo definir constructores.
Public class ApplicationFrame { . JButton okButton = new JButton(OK); [Link](new ActionListener() { public void actionPerformed(ActionEvent event) { doTheOKThing(); } } ); . private void doTheOKThing() { // heres where I handle the OK } .
Ejemplo
Cdigo
// importa los smbolos de AWT and Swing import [Link].*; import [Link].*; import [Link].*; public class SimpleEvents extends JFrame { static final int WIDTH=350; //ancho y alto del frame static final int HEIGHT=180; // Declara JTextField para entrar texto JTextField textField; // Declara JTextArea p/recibir lneas de textField JTextArea textList; // Declara JScrollPane para JTextArea JScrollPane pane; // Constructor:aqu se hace casi todo el trabajo public SimpleEvents(String lab) { // llama el constructor de Jframe:pone etiqueta super(lab);
Cdigo
/******* Crea un contedor para textField ****/ // Instancia un JPanel JPanel textPanel = new JPanel(); // le pone un borde (por defecto no tiene) [Link]([Link]()); // Set el layout del textPanel a BorderLayout [Link](new BorderLayout()); // Crea una etiqueta y la aade al panel JLabel textTitle =new JLabel("Type and hit <ENTER>"); [Link](textTitle, [Link]); // Instancia un JTextField y aade a textPanel textField = new JTextField(); [Link](textField, [Link]); // Aade un strut al textPanel como margen inferior [Link]([Link](6)); // Crea un contenedor p/ textArea // Instancia un JPanel JPanel listPanel = new JPanel();
Cdigo
// aade borde [Link] ([Link]()); // Set el layout del textPanel [Link](new BoxLayout(listPanel,BoxLayout.Y_AXIS)); // Crea una etiqueta y aade al panel JLabel title = new JLabel("Text List"); [Link](title); // Aade un strut al BoxLayout [Link]([Link](10)); // Instancia una JTextArea sin texto inicial // 6 filas, 10 columnas, y vertical scrollbars textList=new JTextArea("", 6, 10); // la hace read-only [Link](false); // Aade textList a listPanel pane = new JScrollPane (textList); [Link](pane);
Cdigo
// Aade un strut a listPanel como margen inferior [Link]([Link](6)); /* Aade un listener a textField cuando se pulse ENTER copia el texto de textField al area de texto. Los componentes estn interrelacionados*/ [Link](new ActionListener() { public void actionPerformed(ActionEvent e) { // Aade el texto de textField a textList [Link]([Link]()); [Link]("\n"); // Reset el textField [Link](""); }}); // Aade los 2 paneles al frame, separados por strut Container c = getContentPane(); [Link] (new FlowLayout()); [Link](textPanel); [Link]([Link](30)); [Link](listPanel);}
Cdigo
public static void main(String args[]) { SimpleEvents frame = new SimpleEvents("Simple Events Example"); // standard adapter usado en casi todas las // aplicaciones para cerrar la ventana [Link](new WindowAdapter() { public void windowClosing(WindowEvent e) { [Link](0); } }); // set el tamao de frame y lo muestra [Link](WIDTH, HEIGHT); [Link](true); } }
Command Pattern
Command encapsula un requerimiento en un objeto, permitiendo parametrizar clientes con diferentes
requerimientos, hacer una cola o registro de ellos y
permitir por lo tanto hacer undo/redo.
Command Pattern
Swing Action
Command permite desacoplar el objeto invocante
(Invoker) del receptor (Receiver, el que conoce cmo atender el requerimiento).
Swing implementa el patrn Command con objectos
de clases que implementen Action, por ej., subclases de AbstractAction. Siempre que selecciona un tem de un men o pulsa un botn, estos objetos action emiten un requerimiento de una accin especfica de la
aplicacin.
Swing Action
Puede configurar un objeto JBbutton mediante una implementacin concreta de Action que delega el requerimiento al Receiver. JButton acta como Invoker llamando al mtodo actionPerformed de un objeto concreto Action y no le importa qu objeto ejecuta realmente el comando requerido. El objeto Action delega el llamado al objeto Receiver que sabe cmo procesar el requerimiento, pero no quin lo hizo. El Receiver puede ser modificado o ser otro objeto y no afectar el cdigo en JButton y del mismo modo se puede aadir el pedido de nuevos objetos, sin afectar al Receiver.
Ejemplo Swing Action
Cdigo
import [Link].*; import [Link].*; import [Link].*; class ColorAction extends AbstractAction{ private Component target; public ColorAction(String name, Icon icon,Color c, Component comp) {putValue([Link], name); putValue(Action.SMALL_ICON, icon); putValue("Color", c); target = comp;}
Cdigo
public void actionPerformed(ActionEvent evt) { Color c = (Color)getValue("Color"); [Link](c);
[Link]();}}
class ActionButton extends JButton{ public ActionButton(Action a){ setText((String)[Link]([Link])); Icon icon = (Icon)[Link](Action.SMALL_ICON); if (icon != null) setIcon(icon); addActionListener(a);}}
Cdigo
class SeparateGUIFrame extends JFrame{ public SeparateGUIFrame(){ setTitle("SeparateGUITest");
setSize(300, 200);
addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { [Link](0);}}); JPanel panel = new JPanel(); Action blueAction = new ColorAction("Blue",new ImageIcon( "[Link]"),[Link], panel);
Cdigo
Action yellowAction = new ColorAction("Yellow",
new ImageIcon("[Link]"),[Link], panel); Action redAction = new ColorAction("Red", new ImageIcon("[Link]"),[Link], panel); [Link](new ActionButton(yellowAction)); [Link](new ActionButton(blueAction)); [Link](new ActionButton(redAction)); [Link](yellowAction, [Link](KeyEvent.VK_Y, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
Cdigo
[Link](blueAction,
[Link](KeyEvent.VK_B, 0),
JComponent.WHEN_IN_FOCUSED_WINDOW); [Link](redAction,
[Link](KeyEvent.VK_R, 0),
JComponent.WHEN_IN_FOCUSED_WINDOW); Container contentPane = getContentPane();
[Link](panel);
JMenu m = new JMenu("Color"); [Link](yellowAction);
Cdigo
[Link](blueAction);
[Link](redAction); JMenuBar mbar = new JMenuBar(); [Link](m); setJMenuBar(mbar);}} public class SeparateGUITest{ public static void main(String[] args){ JFrame frame = new SeparateGUIFrame(); [Link]();}}
Threads y Swing
Si su programa crea y se refiere a la GUI en forma
correcta, probablemente no tenga que preocuparse de este tema.
Si su programa crea threads para realizar tareas
que afecten la GUI, o si manipula la GUI ya visible en respuesta a algo que no sea un evento standard, sea cuidadoso!
Regla
Regla del thread nico: Una vez que un componente se realiz, todo cdigo
que pueda afectarlo o dependa del estado del mismo,
debera ejecutarse en el event-dispatching thread . Hay unas pocas excepciones:
[Link]
La historia real
import [Link].*; public class HelloGUI { public static void main (String[ ] arg) { [Link] (About to make GUI); Frame f = new Frame (Hello GUIs); [Link]( 200, 200 ); [Link](); [Link] (Finished making GUI); }// main }// class HelloGUI
Usualmente se piensa en un programa como un nico conjunto lineal de pasos a ejecutar, aunque ocurre algo especial cuando se crean objetos grficos.
Event-dispatching thread
import [Link].*; public class HelloGUI { public static void main (String[ ] arg) { [Link] (About to make GUI); Frame f = new Frame (Hello GUIs); [Link]( 200, 200 ); [Link](); [Link] (Finished making GUI); }// main }// class HelloGUI
Las dos reas de un programa con GUI
import [Link].*; public class HelloGUI { public static void main (String[ ] arg) { [Link] (About to make GUI); Frame f = new Frame (Hello GUIs); [Link]( 200, 200 ); [Link](); [Link] (Finished making GUI); }// main }// class HelloGUI
El cdigo que escribe en main() Y otros mtodos El cdigo que Java provee para manejar la parte grfica de la aplicacin.
Su cdigo
Graphics
Event-dispatching thread
import [Link].*; public class HelloGUI { public static void main (String[ ] arg) { [Link] (About to make GUI); Frame f = new Frame (Hello GUIs); [Link]( 200, 200 ); [Link](); [Link] (Finished making GUI); }// main }// class HelloGUI
Mouse Click
callback El cdigo para manejar este evento
Event-dispatching thread
Registro de eventos antes que ocurren
Event-dispatching thread
ActionListener
import [Link].*; public class HelloGUI { public static void main (String[ ] arg) { [Link] (About to make GUI); Frame f = new Frame (Hello GUIs); [Link]( 200, 200 ); [Link](); [Link] (Finished making GUI); }// main }// class HelloGUI
public void actionPerformed (ActionEvent e) { // code doing something }
callback
Este mtodo DEBE estar aqu, tal que Java lo sabe y puede callback al mismo.
Cuando conviene usar threads?
Para mover, fuera del thread principal, una tarea de inicializacin que requiera mucho tiempo y para que la GUI sea ms rpida (ej.: clculos intensivos, I/O en disco tal como carga de imgenes). Para mover fuera del event-dispatching thread, tareas que requieran mucho tiempo, de forma tal que la GUI permanezca sensible.
( Ejecutar el cdigo de manejo de eventos en un nico thread garantiza que el manejo de cada uno de ellos terminar de ejecutarse antes del manejo del siguiente)
Para ejecutar una operacin repetidamente, usualmente a iguales intervalos de tiempo. Para esperar mensajes de otros programas.
Programas Multi-threads
Debera analizar el cdigo de su programa y documentar desde cul thread se invoca cada mtodo. Si no est seguro si su cdigo se est ejecutando en el thread de eventos use [Link]() que retorna true si se est ejecutando en el EDT. Invoque [Link] (preferido, retorna inmediatamente sin esperar que el EDT ejecute el cdigo) o [Link] desde cualquier thread para requerir que el EDT ejecute cierto cdigo que involucre un acceso a la GUI.
Programas Multi-threads
Ejemplo: un thread que necesita acceder a un par de text fields: void printTextField() throws Exception { final String[] myStrings = new String[2]; Runnable getTextFieldText = new Runnable() { public void run() { myStrings[0] = [Link](); myStrings[1] = [Link](); } }; [Link](getTextFieldText); [Link](myStrings[0] + " " + myStrings[1]); }
Programas Multi-threads
SwingWorker Crea un thread para ejecutar operaciones que requieran mucho tiempo. Despus que las mismas finalizan, da la opcin de ejecutar cdigo adicional en el EDT. no est en Swing release. mtodos construct() and finish() [Link] Se suele usar si se necesita actualizar un componente despus de un delay o a intervalos regulares.
Ejemplo animacin
Cdigo
import [Link].*; import [Link].*; import [Link].*; public class Components extends JPanel { int position = 10; public void paintComponent(Graphics g) { [Link](g); [Link](position,10,position + 20,90); position ++; if(position > 300) position = 10;}}
Cdigo
import [Link].*; import [Link].*; import [Link].*; public class MovingLine extends JFrame implements ActionListener {Timer timer; boolean frozen = false; Component moving; MovingLine(int fps, String windowTitle) { super(windowTitle); int delay = (fps > 0) ? (1000 / fps) : 100; timer = new Timer(delay, this);
Cdigo
[Link](0); [Link](true); addWindowListener(new WindowAdapter() { public void windowIconified(WindowEvent e) { stopAnimation();} public void windowDeiconified(WindowEvent e) { startAnimation();}
public void windowClosing(WindowEvent e) {
[Link](0);} }); Component contents = new Components();
Cdigo
[Link](new MouseAdapter() { public void mousePressed(MouseEvent e) { if (frozen) { frozen = false; startAnimation(); } else { frozen = true; stopAnimation(); }} }); getContentPane().add(contents, [Link]);} //Can be invoked by any thread (since timer is thread-safe).
Cdigo
public void startAnimation() { if (frozen) { //Do nothing. The user has requested that we //stop changing the image. } else {//Start animating! if (![Link]()) { [Link](); } } }
//Can be invoked by any thread (since timer is thread-safe).
public void stopAnimation() { //Stop the animating thread.
if ([Link]()) { [Link]();} }
Cdigo
public void actionPerformed(ActionEvent e) { //Advance the animation frame. repaint(); } public static void main(String args[]) { int fps = 10; //Get frames per second from the command line argument. if ([Link] > 0) {try {
fps = [Link](args[0]);
} catch (Exception e) {} } MovingLine moving = new MovingLine(fps, "Moving line with Timer --" + "Click to pause or continue");
Cdigo
[Link](); [Link](600,200); [Link](true);
//It's OK to start the animation here because
//startAnimation can be invoked by any thread. [Link](); }}
Applet, Timer y SwingWorker
Cdigo
import [Link].*; import [Link].*; import [Link].*; import [Link].*;
public class TumbleItem extends JApplet implements ActionListener {
int loopslot = -1; //[Link] frame actual String dir; //[Link] desde el cual cargo las 17 imgenes //long. de pausas //offset entre loops //offset actual
Timer timer;
int pause; int offset; int off;
Cdigo
int speed; //[Link] animacin
int nimgs;
int width;
//[Link] imgenes a animar
//ancho del content pane del applet
JComponent contentPane;
ImageIcon imgs[]; //imgenes
int maxWidth; //ancho [Link] las imgenes boolean finishedLoading = false;
JLabel statusLabel;
static Color[] labelColor = { [Link], [Link], [Link], [Link], [Link], [Link], [Link], [Link],[Link], [Link] };
Cdigo
public void init() { //parmetros del applet..... //Anima de derecha a izquierda si offset es negativo. width = getSize().width;
if (offset < 0) {off = width - maxWidth;}
//dibuja la imagen actual a un particular offset. contentPane = new JPanel() {
public void paintComponent(Graphics g) {[Link](g);
if (finishedLoading &&(loopslot > -1) && (loopslot < nimgs)) { imgs[loopslot].paintIcon(this, g, off, 0);}}}; [Link]([Link]);
Cdigo
setContentPane(contentPane); //"Loading Images..." label statusLabel = new JLabel("Loading Images...", [Link]); [Link](labelColor[0]); [Link](new BorderLayout()); [Link](statusLabel, [Link]); //Set up timer, no comience h/cargar todas las imgenes
timer = new Timer(speed, this);
[Link](pause); imgs = new ImageIcon[nimgs];
[Link](); //comienza la animacin
Cdigo
//Carga de imgenes puede tomar mucho tiempo, las carga en un SwingWorker thread final SwingWorker worker = new SwingWorker() { public Object construct() {
//Imgenes numeradas de 1 a nimgs,
for (int i = 0; i < nimgs; i++) {imgs[i] = createFrame(i+1);} finishedLoading = true; return imgs;} public void finished() {//saca la etiqueta "Loading images" [Link](); [Link](); loopslot = -1;}};
Cdigo
[Link]();}
protected ImageIcon createFrame(int imageNum) {
String path = dir + "/T" + imageNum + ".gif"; URL imgURL = [Link]().getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);} else {[Link]("Couldn't find file: " + path);
return null;}}
public void start() { if (finishedLoading && (nimgs > 1)) { [Link]();}} public void stop() {[Link]();}
Cdigo
//actualiza [Link] frame y offset. //si es el ltimo frame, hacer una pausa larga entre loops public void actionPerformed(ActionEvent e) { loopslot++; if (!finishedLoading) { int colorIndex = loopslot % [Link]; [Link](labelColor[colorIndex]);
return;}
Cdigo
if (loopslot >= nimgs) { loopslot = 0; off += offset; if (off < 0) { off = width - maxWidth; } else if (off + maxWidth > width) { off = 0;}}
[Link]();
if (loopslot == nimgs - 1) { [Link]();}}
Cdigo