370 likes | 782 Views
IGU en Java: Modelo de Eventos. Ingeniería de la Programación Práctica 4. Contenido. Introducción Elementos del modelo de eventos en Java Jerarquía de eventos. Fuentes de eventos. Receptores de eventos e interfaces receptoras Registro de receptores. Clases adaptadoras.
E N D
IGU en Java:Modelo de Eventos Ingeniería de la Programación Práctica 4
Contenido • Introducción • Elementos del modelo de eventos en Java • Jerarquía de eventos. • Fuentes de eventos. • Receptores de eventos e interfaces receptoras • Registro de receptores. • Clases adaptadoras. • Estrategias de programación del receptor.
Objetivos Comprender el modelo de eventos de Java, basado en delegación. Seleccionar la mejor estrategia para programar los ‘listeners’ de eventos. Implementar eventos básicos para una IGU.
Introducción • Todo sistema operativo que utiliza interfaces gráficas de usuario debe estar constantemente monitorizando el entorno para capturar y tratar los eventos que se producen. • El sistema operativo informa de estos eventos a los programas que se están ejecutando y entonces cada programa decide qué hace para dar respuesta a esos eventos. • Los eventos pueden estar producidos por el sistema o por el usuario. • Los programas que gestionan las interacciones del usuario de esta forma se dice que están dirigidos por eventos.
Introducción • Cada vez que el usuario realiza una determinada acción sobre una aplicación Java (pulsar sobre un botón, pulsar una tecla, pasar el ratón sobre una imagen) se produce un evento que el sistema operativo transmite al entorno de ejecución de Java. • Java crea un objeto de una determinada clase de evento, el cual es transmitido a un determinado método para que lo gestione. • El modelo de eventos de Java está basado en delegación (la responsabilidad de gestionar un evento que ocurre en un objeto – fuente/source- la tiene otro objeto-receptor/listener-). 1. Se registra como oyente Objeto fuente Objeto oyente 3. Cuando se produce un evento se comunica a todos los oyentes, mediante la invocación de un método del oyente (al que se le pasa como parámetro el evento) 2. Lo apunta en la lista de oyentes
Introducción • Cada componente de la interfaz de usuario puede generar varios tipos de eventos. • Java permite varios receptores para un mismo tipo de evento de un emisor. • De igual forma varios emisores se pueden asociar a un único receptor. Objeto oyente 1 Objeto fuente Objeto oyente 2 Objeto fuente 1 Objeto oyente Objeto fuente 2
Introducción textField1 manejador JTextField object publicvoid actionPerformed( java.awt.event.ActionEvent e) { // código de manejo del evento } listenerList Esta referencia la crea la sentencia: textField1.addActionListener( manejador);
Elementos del modelo de eventos de Java • Jerarquías de clases de Eventos • Fuentes de Eventos (event sources) • Receptores de Eventos (event listener) • Interfaces listener. • Registrar eventos. • Adaptadores (adapter classes)
Jerarquías de eventos Object EventObject AWTEvent ItemEvent (1) TextEvent (1) AdjustmentEvent (1) ComponentEvent ActionEvent (1) WindowEvent PaintEvent ContainerEvent InputEvent FocusEvent KeyEvent MouseEvent • Cada vez que se produce un evento dicho evento se representa como una instancia de una clase descendiente de AWTEvent.
Fuentes de eventos • La fuente (source) de un evento es el objeto que lo detecta y lo comunica al objeto receptor (listener). • Ejemplos de fuentes y eventos generados
Eventos generados por componentes AWT (2) • Hay componentes de AWT que no poseen eventos propios (TextArea), pero todos los eventos de una superclase (en la jerarquía AWT) se aplican a la subclase. • Con la consideración anterior un TextArea puede generar los siguientes eventos (todos heredados):
Receptores de eventos • Cuando ocurre un evento un objeto fuente puede llamar a uno o varios objetos receptores. • Los receptores (listeners) de eventos son objetos de una clase determinada que implementa un interface específico llamado interface listener. • Existe una interface listener por cada tipo de evento.
Eventos generados por componentes Swing • Los eventos generados por los componentes Swing son de dos tipos: • Una primera clase que engloba los oyentes que todos los componentes Swing pueden soportar: • Component Listener: Oyentes para el cambio en el tamaño, posición o visibilidad de un componente. • Focus Listener: Oyentes para manejar la perdida o ganancia de foco del teclado. • Key Listener: Oyentes para detectar pulsaciones de teclado; los eventos de teclado son activados únicamente por el componente que tiene el foco del teclado. • Mouse Listener: Oyentes para “clicks” del ratón, pulsar un botón del ratón, soltar un botón del ratón, entrar en el área de dibujo del componente, salir de la misma. • Mouse-motion Listener: Cambiar la posición del cursor del ratón sobre un componente. • Mouse-wheel Listener: Movimiento de la rueda del ratón sobre un componente. • Hierarchy Listener: Oyentes para los cambios en la jerarquía de un componente. • Hierarchy Bounds Listener: Oyentes para el cambio de posición y de tamaño. • Una segunda clase que engloba oyentes específicos para cada tipo de componente.
Registro del receptor • Cuando ya disponemos del objeto fuente y del objeto receptor es necesario asociarlos, es decir, hay que registrar el objeto receptor de eventos con el objeto fuente de los mismos: • El objeto receptor debe implementar una determinada interface listener. Los métodos de la interfaz serán llamados cuando se produzca el evento. • Con la instrucción remove<Evento>Listener(ObjetoReceptor)se puede eliminar el objeto oyente. ObjetoFuenteEventos.add<Event>Listener(ObjetoReceptorEventos) Se cambia por el evento que se quiera recibir
Adaptadores • Los adaptadores (adapters class) simplifican la programación de las clases receptoras ya que no es necesario implementar todos los métodos de las interfaces listeners. • Existe una clase adaptadora (xxxAdapter) por cada interface (xxxListener) que contenga más de un método: MouseAdapter, WindowAdapter, KeyAdapter, MouseMotionAdapter, FocusAdapter, ContainerAdapter y ComponentAdapter, etc. • Las clases adaptadoras implementan (mediante código vacío) todos los métodos de la correspondiente interfaz. • Las adaptadoras se extienden y se incluyen los métodos de la interfaz que sean de interés.
Programación del receptor: alternativas • Se pueden utilizar tres estrategias: • Crear el receptor como clase interna, donde una clase interna es una clase definida dentro de otra, pudiendo acceder a todos los métodos y atributos de la clase que la contiene. • Crear el receptor como una clase independiente. • La propia ventana implemente el receptor.
1. Receptor: clase interna • Ejemplo: Cambiar el color de fondo de un formulario al pulsar un botón: gris-> blanco -> gris (proyecto visual editor ejemplo1) • El emisor es un JButton y el evento generado es del tipo ActionEvent. • El receptor debe implementar a la interface ActionListener que tiene únicamente el método: void actionPerformed( ActionEvent e) • Dos soluciones: • 1.a clase interna programada manualmente. (ejemplo 1) • 1.b clase anónima interna, es lo que genera visual editor. (ejemplo 2)
1. Receptor: clase interna publicclass VentanaPrincipal extends JFrame { privatestaticfinallongserialVersionUID = 1L; private JPanel jContentPane = null; private JButton Boton = null; /* código añadido */ privateboolean colorgris = true; … /* código inicialización del componente */ privatevoid initialize() { this.setSize(300, 200); this.setContentPane(getJContentPane()); this.setTitle("Ejemplo 1"); ProcesaBoton pb = new ProcesaBoton(); this.Boton.addActionListener(pb); } … //* receptor clase local class ProcesaBoton implements ActionListener { publicvoid actionPerformed(ActionEvent e) { if (colorgris) jContentPane.setBackground(Color.white); else jContentPane.setBackground(Color.lightGray); colorgris = !colorgris; } } }
1. Receptor código generado por eclipse • Eclipse genera una clase anónima interna, vea ejemplo 2. private JButton getBoton() { if (Boton == null) { Boton = new JButton(); Boton.setText("Cambiar color"); Boton.addActionListener( new java.awt.event.ActionListener() { publicvoid actionPerformed(java.awt.event.ActionEvent e) { // TODO Auto-generated Event stub actionPerformed() if (colorgris) jContentPane.setBackground(Color.white); else jContentPane.setBackground(Color.lightGray); colorgris = ! colorgris; } }); } return Boton; } Clase anónima interna
2. Receptor: clase separada • Al ser clases separadas no se puede acceder a las características de la clase que contiene al emisor, si éstas se quieren modificar se pasa una referencia a la ventana de la aplicación, en la llamada al constructor del receptor (ejemplo 3). public class Ejemplo3 extends JFrame { // el receptor se registra igual en el caso 1. private void initialize() { this.setSize(300, 200); this.setContentPane(getJContentPane()); this.setTitle("Ejemplo 3"); // registrar el receptor... ProcesaBoton pb = new ProcesaBoton(this); this.Boton.addActionListener(pb); } public void CambiarColor(){ } } class ProcesaBoton implements ActionListener { private Ejemplo3 Marco; public ProcesaBoton(Ejemplo3 MarcoPrincipal) { Marco = MarcoPrincipal;} publicvoid actionPerformed(ActionEvent e) { Marco.CambiarColor();} }
2. Receptor: clase separada pero generada automáticamente • Algunas herramientas como JBuilder o JDeveloper generan el siguiente código. public class Ejemplo4_Marco extends JFrame { JPanel contentPane; JButton Boton = new JButton(); public Ejemplo4_Marco() {… } private void jbInit() throws Exception { contentPane = (JPanel) getContentPane(); contentPane.setLayout(boxLayout21); setSize(new Dimension(400, 300)); setTitle("Ejemplo 4"); Boton.setText("Cambiar Color"); Boton.addActionListener( new Ejemplo4_Marco_Boton_actionAdapter(this)); contentPane.add(Boton, null); } public void Boton_actionPerformed(ActionEvent e) { } }
2. Receptor: clase separada pero generada automáticamente • La solución es similar a la del ejemplo 3 class Ejemplo4_Marco_Boton_actionAdapter implements ActionListener { private Ejemplo4_Marco adaptee; public Ejemplo4_Marco_Boton_actionAdapter(Ejemplo4_Marco adaptee) { this.adaptee = adaptee; } public void actionPerformed(ActionEvent e) { adaptee.Boton_actionPerformed(e); } }
3. Receptores: la ventana implementa el receptor publicclass Ejemplo5 extends JFrame implements ActionListener { privatestaticfinallongserialVersionUID = 1L; private JPanel jContentPane = null; private JButton Boton = null; privateboolean colorgris = true; … // en azul código añadido a mano. private JButton getBoton() { if (Boton == null) { Boton = new JButton(); Boton.setText("Pulsa"); Boton.setBounds(new Rectangle(16, 46, 66, 26)); Boton.addActionListener(this);} return Boton; } … publicvoid actionPerformed(ActionEvent e) { if (colorgris) jContentPane.setBackground(Color.white); else jContentPane.setBackground(Color.lightGray); colorgris = ! colorgris; } … }
Ejemplo clases adaptadoras • Ejemplo: validar la longitud de un campo de texto (JTextField) cuando pierde el foco (FocusEvent)*. • El receptor tiene que ser un objeto que implemente a la interface FocusListener que tiene los métodos: public void focusLost( FocusEvent e) public void focusGained( FocusEvent e) • Utilizaremos el adaptador FocusAdapter, ya que únicamente queremos implementar el método focusLost. • El receptor será una clase separada. *Lo habitual es programar un manejador para los botones class ManejaFocoDNI extends java.awt.event.FocusAdapter { private Ejemplo6 adaptador; public ManejaFocoDNI(Ejemplo6 adaptador1) { this.adaptador = adaptador1; } publicvoid FocusLost(FocusEvent e) { // código para manejar la pérdida de foco. System.out.println("Perdida de foco"); } }
Otro ejemplo: cerrar una ventana this.addWindowListener(new java.awt.event.WindowAdapter() { publicvoid windowClosing(java.awt.event.WindowEvent e) { if (jtfText.getText().compareTo("")==0) ; else { int resp; resp=javax.swing.JOptionPane.showConfirmDialog (null, “¿Estas seguro?","Aviso",javax.swing.JOptionPane.OK_CANCEL_OPTION); if (resp==JOptionPane.OK_OPTION) System.exit(0); } } }); }
Referencias • Tutorial sobre Swing, accesible en: http://www.programacion.com/java/tutorial/swing/ • Tutorial sobre Java, con eventos. http://www.itapizaco.edu.mx/paginas/JavaTut/froufe/introduccion/indice.html • Creating a GUI with JFC/Swing, accesible en: http://java.sun.com/docs/books/tutorial/uiswing/TOC.html
Ejercicio • Consiste en implementar la funcionalidad de la Gestión de Máquinas de la práctica 3, haciendo uso de la clase ModeloTablaMaquina. • Para ello debemos implementar los manejadores de eventos de los diferentes botones y opciones de menú, haciendo que ejecuten los métodos del objeto ModeloTablaMaquina asociado en la Práctica 3 a la tabla de la Ventana Principal.
Ejercicio • Punto 1: Enlace entre ventanas. Botones Añadir y Detalle. VentanaDetalle v=new VentanaDetalle(tabla.getModel()); v.setVisible(true); Debe pasarle como parámetro el modelo de la tabla Debe pasarle como parámetro la máquina seleccionada Maquina m= modelo.recuperaMaquinaPorPosicion (tabla.getSelectedRow()); VentanaDetalle v=new VentanaDetalle(m); v.setVisible(true);
Ejercicio • Punto 2: Constructor y Botones Ventana Detalle. • Constructor: Sobrecargado. Recibe un ModeloTablaMaquina o una Maquina. En el caso de recibir una Maquina visualiza sus datos en la caja de texto y deshabilita el botón Guardar. • Botón Guardar. Habilitado sólo cuando el constructor recibe un ModeloTablaMaquina (i.e. se accede con el botón Añadir de la Ventana Principal). Crea una Máquina a partir de los campos de texto y la guarda mediante el método addMaquina del la clase ModeloTablaMaquina. Si todo es correcto cierra la Ventana Detalle. • Botón Volver: Cierra la Ventana Detalle
Ejercicio • Punto 3: Menús de la Ventana Principal. • Menu Archivo • Guardar: serializa el listado de máquina mediante el método guardaMaquinas de la clase ModeloTablaMaquina. Utilice el cuadro de diálogo implementado en la clase JFileChooser para permitir al usuario escoger el fichero que almacena los objetos la primera vez que se guarda. En veces sucesivas usar el fichero seleccionado sin consultar al usuario. • Guardar como: serializa el listado de máquinas mediante el método guardaMaquinas de la clase ModeloTablaMaquina.Utilice el cuadro de diálogo implementado en la clase JFileChooser para permitir escoger siempre el fichero que almacena los objetos. • Cargar de disco: carga el listado de máquina desde disco mediante el método cargaMaquinas de la clase ModeloTablaMaquina.Utilice el cuadro de diálogo implementado en la clase JFileChooser para seleccionar siempre el fichero desde donde cargar. • Salir: Cierra la aplicación • Menú Aspecto: Cambia los colores de la fuente y el fondo de la aplicación. Utilice la paleta de colores implementada en la clase JColorChooser.
Ejercicio • Punto 4: Barra de Herramientas y Botón Borrar de la Ventana Principal. • Barra de Herramientas: sus botones guardan y cargan en disco como los items “Guardar como” y “Cargar de disco” del menú archivo. • Botón Borrar: Elimina la Maquina seleccionada usando el método borraMaquinaPorPosicion de la clase ModeloTablaMaquina.
Ejercicio Adicionalmente: • Utilice la barra de estado para informar al usuario de las acciones realizadas. • Antes de borrar una Máquina pregunte al usuario si está seguro. • Gestione las siguientes excepciones. En cada una de ellas debe informarse al usuario con un mensaje de error en una caja de diálogo (puede utilizar la clase JOptionPane) • Si se intenta añadir una Máquina con alguno de sus campos en blanco, se avisará al usuario con un mensaje. • Si ya existe una Máquina con ese número, se avisará al usuario con un mensaje. Igual que en la Práctica 2. • Cuando se cierra la aplicación (desde el aspa del marco o desde la opción de menú Salir) se deberá detectar si ha habido cambios en la lista de Máquinas desde el último guardado, informando al usuario si desea almacenar en disco dichos cambios. Si es la primera vez que se almacena la lista se pedirá al usuario el nombre del fichero (como en la opción de menú Guardar como…)