Você está na página 1de 68

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

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,

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

Eventos Swing
Swing tiene adems su propio paquete de manejo de eventos: javax.swing.event Casi todos estos nuevos eventos estn relacionados con la arquitectura MVC Ejemplo: TreeModelEvent

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 (java.util.EventListener). 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( ctionEvent e); A } public interface ItemListener { public void itemStateChanged( temEvent e); I } public interface ComponentListener { public void componentHidden( omponentEvent e); C public void componentMoved( omponentEvent e); C public void componentResized( omponentEvent e); C public void componentShown( omponentEvent e); C }

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

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

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
public una clase que la implemente) extiendaclass 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); okButton.addActionListener(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

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

Clases internas con nombre


Se definen como las clases normales No pueden ser public
public class ApplicationFrame { . class ButtonHandler implementsActionListener { Public void actionPerformed (ActionEvent e) { doTheOKThing(); } } private void doTheOKThing() { // heres where I handle OK } . JButton okButton = new JButton(OK); okButton.addActionListener(new ButtonHandler()); // create inner class listener .

Clases internas con nombre


public class MyClass extends JPanel { anObject.addMouseListener(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 = newJButton(OK); okButton.addActionListener(new ActionListener() { public void actionPerformed( ctionEvent event) { A doTheOKThing(); } } ); . private void doTheOKThing() { // heres where I handle the OK } .

Ejemplo

Cdigo
// importa los smbolos de AWT and Swing import java.awt.*; import java.awt.event.*; import javax.swing.*; 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) textPanel.setBorder(BorderFactory.createEtchedBorder()); // Set el layout del textPanel a BorderLayout textPanel.setLayout(new BorderLayout()); // Crea una etiqueta y la aade al panel JLabel textTitle =new JLabel("Type and hit <ENTER>"); textPanel.add(textTitle, BorderLayout.NORTH); // Instancia un JTextField y aade a textPanel textField = new JTextField(); textPanel.add(textField, BorderLayout.SOUTH); // Aade un strut al textPanel como margen inferior textPanel.add(Box.createVerticalStrut(6)); // Crea un contenedor p/ textArea // Instancia un JPanel JPanel listPanel = new JPanel();

Cdigo
// aade borde listPanel.setBorder (BorderFactory.createEtchedBorder()); // Set el layout del textPanel listPanel.setLayout(new BoxLayout(listPanel,BoxLayout.Y_AXIS)); // Crea una etiqueta y aade al panel JLabel title = new JLabel("Text List"); listPanel.add(title); // Aade un strut al BoxLayout listPanel.add(Box.createVerticalStrut(10)); // Instancia una JTextArea sin texto inicial // 6 filas, 10 columnas, y vertical scrollbars textList=new JTextArea("", 6, 10); // la hace read-only textList.setEditable(false); // Aade textList a listPanel pane = new JScrollPane (textList); listPanel.add(pane);

Cdigo
// Aade un strut a listPanel como margen inferior listPanel.add(Box.createVerticalStrut(6)); /* Aade un listener a textField cuando se pulse ENTER copia el texto de textField al area de texto. Los componentes estn interrelacionados*/ textField.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // Aade el texto de textField a textList textList.append(textField.getText()); textList.append("\n"); // Reset el textField textField.setText(""); }}); // Aade los 2 paneles al frame, separados por strut Container c = getContentPane(); c.setLayout (new FlowLayout()); c.add(textPanel); c.add(Box.createHorizontalStrut(30));

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 frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); // set el tamao de frame y lo muestra frame.setSize(WIDTH, HEIGHT); frame.setVisible(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

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.

Ejemplo Swing Action

Cdigo
import java.awt.*; import java.awt.event.*; import javax.swing.*; class ColorAction extends AbstractAction{ private Component target; public ColorAction(String name, Icon icon,Color c, Component comp) {putValue(Action.NAME, name); putValue(Action.SMALL_ICON, icon); putValue("Color", c); target = comp;}

Cdigo
public void actionPerformed(ActionEvent evt) { Color c = (Color)getValue("Color"); target.setBackground(c); target.repaint();}} class ActionButton extends JButton{ public ActionButton(Action a){ setText((String)a.getValue(Action.NAME)); Icon icon = (Icon)a.getValue(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) { System.exit(0);}}); JPanel panel = new JPanel(); Action blueAction = new ColorAction("Blue",new ImageIcon( "blue-ball.gif"),Color.blue, panel);

Cdigo
Action yellowAction = new ColorAction("Yellow", new ImageIcon("yellow-ball.gif"),Color.yellow, panel); Action redAction = new ColorAction("Red", new ImageIcon("red-ball.gif"),Color.red, panel); panel.add(new ActionButton(yellowAction)); panel.add(new ActionButton(blueAction)); panel.add(new ActionButton(redAction)); panel.registerKeyboardAction(yellowAction, KeyStroke.getKeyStroke(KeyEvent.VK_Y, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);

Cdigo
panel.registerKeyboardAction(blueAction, KeyStroke.getKeyStroke(KeyEvent.VK_B, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); panel.registerKeyboardAction(redAction, KeyStroke.getKeyStroke(KeyEvent.VK_R, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); Container contentPane = getContentPane(); contentPane.add(panel); JMenu m = new JMenu("Color"); m.add(yellowAction);

Cdigo
m.add(blueAction); m.add(redAction); JMenuBar mbar = new JMenuBar(); mbar.add(m); setJMenuBar(mbar);}} public class SeparateGUITest{ public static void main(String[] args){ JFrame frame = new SeparateGUIFrame(); frame.show();}}

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

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:

La historia real
import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (About to make GUI); Frame f = new Frame (Hello GUIs); f.setSize( 200, 200 ); f.show(); System.out.println (Finished making GUI); }// main }// class HelloGUI

Usualmente se piensa en un programa como un nico conjunto lineal de pasos a ejecutar, aunque ocurre algo

Event-dispatching thread
import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (About to make GUI); Frame f = new Frame (Hello GUIs); f.setSize( 200, 200 ); f.show(); System.out.println (Finished making GUI); }// main }// class HelloGUI

Las dos reas de un programa con GUI


import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (About to make GUI); Frame f = new Frame (Hello GUIs); f.setSize( 200, 200 ); f.show(); System.out.println (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 java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (About to make GUI); Frame f = new Frame (Hello GUIs); f.setSize( 200, 200 ); f.show(); System.out.println (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 java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (About to make GUI); Frame f = new Frame (Hello GUIs); f.setSize( 200, 200 ); f.show(); System.out.println (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

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 SwingUtilities.isEventDispatchThread() que retorna true si se est ejecutando en el EDT. Invoque SwingUtilities.invokeLater (preferido, retorna inmediatamente sin esperar que el EDT ejecute el cdig o SwingUtilities.invokeAndWait 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] = textField0.getText(); myStrings[1] = textField1.getText(); } }; SwingUtilities.invokeAndWait(getTextFieldText); System.out.println(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() javax.swing.Timer Se suele usar si se necesita actualizar un componente despus de un delay o a intervalos regulares.

Ejemplo animacin

Cdigo
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Components extends JPanel { int position = 10; public void paintComponent(Graphics g) { super.paintComponent(g); g.drawLine(position,10,position + 20,90); position ++; if(position > 300) position = 10;}}

Cdigo
import java.awt.*; import java.awt.event.*; import javax.swing.*; 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
timer.setInitialDelay(0); timer.setCoalesce(true); addWindowListener(new WindowAdapter() { public void windowIconified(WindowEvent e) { stopAnimation();} public void windowDeiconified(WindowEvent e) { startAnimation();} public void windowClosing(WindowEvent e) { System.exit(0);} }); Component contents = new Components();

Cdigo
contents.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (frozen) { frozen = false; startAnimation(); } else { frozen = true; stopAnimation(); }} }); getContentPane().add(contents, BorderLayout.CENTER);}

Cdigo
public void startAnimation() { if (frozen) { //Do nothing. The user has requested that we //stop changing the image. } else {//Start animating! if (!timer.isRunning()) { timer.start(); } } } //Can be invoked by any thread (since timer is threadsafe). public void stopAnimation() { //Stop the animating thread.

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 (args.length > 0) {try { fps = Integer.parseInt(args[0]); } catch (Exception e) {} } MovingLine moving = new MovingLine(fps, "Moving line

Cdigo
moving.pack(); moving.setSize(600,200); moving.setVisible(true); //It's OK to start the animation here because //startAnimation can be invoked by any thread. moving.startAnimation(); }}

Applet, Timer y SwingWorker

Cdigo
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.net.*; public class TumbleItem extends JApplet implements ActionListener { int loopslot = -1; //nro.de frame actual String dir; imgenes Timer timer; int pause; //long. de pausas //direct.relativo desde el cual cargo las 17

Cdigo
int speed; int nimgs; int width; //veloc.de animacin //nro.de imgenes a animar //ancho del content pane del applet

JComponent contentPane; ImageIcon imgs[]; //imgenes int maxWidth; //ancho mx.de las imgenes boolean finishedLoading = false; JLabel statusLabel; static Color[] labelColor = { Color.black, Color.black, Color.black, Color.black, Color.black, Color.white, Color.white, Color.white,Color.white, Color.white };

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) {super.paintComponent(g); if (finishedLoading &&(loopslot > -1) && (loopslot < nimgs)) {

Cdigo
setContentPane(contentPane); //"Loading Images..." label statusLabel = new JLabel("Loading Images...", JLabel.CENTER); statusLabel.setForeground(labelColor[0]); contentPane.setLayout(new BorderLayout()); contentPane.add(statusLabel, BorderLayout.CENTER); //Set up timer, no comience h/cargar todas las imgenes timer = new Timer(speed, this); timer.setInitialDelay(pause); imgs = new ImageIcon[nimgs];

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" contentPane.removeAll(); contentPane.repaint();

Cdigo
worker.start();} protected ImageIcon createFrame(int imageNum) { String path = dir + "/T" + imageNum + ".gif"; URL imgURL = this.getClass().getResource(path); if (imgURL != null) { return new ImageIcon(imgURL);} else {System.err.println("Couldn't find file: " + path); return null;}} public void start() { if (finishedLoading && (nimgs > 1)) { timer.restart();}} public void stop() {timer.stop();}

Cdigo
//actualiza nro.de 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 % labelColor.length; statusLabel.setForeground(labelColor[colorIndex]); return;}

Cdigo
if (loopslot >= nimgs) { loopslot = 0; off += offset; if (off < 0) { off = width - maxWidth; } else if (off + maxWidth > width) { off = 0;}} contentPane.repaint(); if (loopslot == nimgs - 1) { timer.restart();}}

Cdigo

Você também pode gostar