Sesión 11

 

1. En esta sesión vamos a realizar dos ejemplos con AWT. El primero de ellos va a simular una calculadora sencilla, con este aspecto:

Figura 1. Apariencia de la calculadora

En el primer cuadro de texto ponemos el primer operando, luego elegimos la operación a realizar (suma '+', resta '-', o multiplicación '*'), y en el segundo cuadro de texto ponemos el segundo operando. Pulsando en Calcular mostraremos el resultado en el tercer cuadro de texto.

  1. Antes de comenzar, lee los apartados 3.1.1 (Introducción a AWT) y 3.1.2 (Gestores de disposición) del tema 3 de teoría.
  2. Echa un vistazo a la clase Calculadora.java que se proporciona en la plantilla de la sesión. Verás que es un subtipo de Frame, y tiene un constructor vacío, y un método main que crea un objeto de ese tipo y lo muestra (método show). Nos falta completar el constructor para definir la ventana que se mostrará.
  3. Lo primero de todo es definir la ventana sobre la que van a ir los controles: crearemos una ventana de 300 de ancho por 150 de alto, con un gestor de tipo GridLayout, con 4 filas y 2 columnas (como se ve en la figura superior):
    public Calculadora()
    {
    	setSize(300, 150);
    	setLayout(new GridLayout(4, 2));
  4. Después colocamos los componentes, por orden, en la rejilla: primero la etiqueta y el cuadro de texto del primer operando, después la etiqueta y el desplegable... etc:
    public Calculadora()
    {
    	...
    
    	// Primer operando
    	Label lblOp1 = new Label("Primer operando:");
    	TextField txtOp1 = new TextField();
    	add(lblOp1);
    	add(txtOp1);
    		
    	// Operador
    	Label lblOper = new Label ("Operador:");
    	Choice operadores = new Choice();
    	operadores.addItem("+");
    	operadores.addItem("-");
    	operadores.addItem("*");
    	add(lblOper);
    	add(operadores);
    		
    	// Segundo operando
    	Label lblOp2 = new Label("Segundo operando:");
    	TextField txtOp2 = new TextField();
    	add(lblOp2);
    	add(txtOp2);
    		
    	// Boton de calcular y cuadro de resultado
    	Button btnRes = new Button ("Calcular");
    	TextField txtRes = new TextField();
    	add(btnRes);
    	add(txtRes);
    }
  5. Llegados a este punto, compila y ejecuta el programa, para comprobar que no hay errores en el código, y para asegurarte de que el programa va a tener la misma apariencia que el de la figura 1 (lógicamente el programa aún no hará nada, sólo verás la ventana).
  6. Lo que nos queda es "hacer que el programa haga algo". Para ello vamos a definir los eventos. Antes de seguir, lee el apartado 3.1.3 (Modelo de eventos en Java) del tema 3 de teoría.
    1. Definimos un evento sobre el botón, para que, al pulsarlo, tome los dos operandos y el operador seleccionado, y muestre el resultado en el cuadro correspondiente:
    public Calculadora()
    {
        ...
        // Evento sobre el botón
        btnRes.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                int op1, op2;
                try
                {
    		// Tomar los dos operandos
                    op1 = Integer.parseInt(txtOp1.getText());
                    op2 = Integer.parseInt(txtOp2.getText());	
    					
    		// Hacer la operacion segun el operador seleccionado
                    if (((String)(operadores.getSelectedItem())).equals("+"))
                        txtRes.setText("" + (op1 + op2));
                       else if (((String)(operadores.getSelectedItem())).equals("-"))
                        txtRes.setText("" + (op1 - op2));
                       else if (((String)(operadores.getSelectedItem())).equals("*"))
                           txtRes.setText("" + (op1 * op2));
                } catch (Exception ex) {
                    txtRes.setText("ERROR EN LOS OPERANDOS");
                }
            }
        });	
    }
    1. El otro evento lo definimos sobre la ventana (el Frame) para hacer que se cierre y termine el programa cuando pulsemos el botón de cerrar:
    public Calculadora()
    {
    	...
    	this.addWindowListener(new WindowAdapter()
    	{
    		public void windowClosing(WindowEvent e)
    		{
    			System.exit(0);
    		}
    	});			
    }
    1. Compila el programa... te dará errores de compilación.
    2. Los errores del paso anterior se deben a que, si accedemos a un control desde dentro de un evento (como por ejemplo a los controles txtOp1, txtOp2, txtRes  o la lista operadores, en el evento del botón), dichos controles no pueden ser variables locales normales. El error de compilación dice que deben declararse variables finales, u otra posibilidad es ponerlas como variables globales de la clase. Es decir, podemos hacer lo siguiente:

      Sustituir estas líneas:
      public Calculadora()
      {
      	...
      	TextField txtOp1 = new TextField();
      	...
      	Choice operadores = new Choice();
      	...
      	TextField txtOp2 = new TextField();
      	...
      	TextField txtRes = new TextField();
      	...
      }

      Por estas:

      public Calculadora()
      {
      	...
      	final TextField txtOp1 = new TextField();
      	...
      	final Choice operadores = new Choice();
      	...
      	final TextField txtOp2 = new TextField();
      	...
      	final TextField txtRes = new TextField();
      	...
      }

      O bien colocarlas fuera del constructor, como variables globales:

      public class Calculadora ...
      {
      	TextField txtOp1 = new TextField();
      	Choice operadores = new Choice();
      	TextField txtOp2 = new TextField();
      	TextField txtRes = new TextField();
      
      	public Calculadora()
      	{
      		...
      	}
      	...
      }
  7. Compila y ejecuta el programa. Prueba su funcionamiento con algunos ejemplos que se te ocurran. Observa también los ficheros .class que se generan: además del principal (Calculadora.class), aparecen dos más (Calculadora$1.class y Calculadora$2.class). ¿Sabrías decir qué son? (AYUDA: observa que aparecen tantos ficheros adicionales como eventos has definido en la aplicación...)

2. Ahora vamos a construir otra aplicación desde cero. Para resumir los pasos que hemos dado en el ejercicio anterior, lee el apartado 3.1.4 (Pasos generales para construir una aplicación gráfica con AWT) del tema 3 de teoría.

La aplicación que vamos  a construir ahora no es más que un bloc de notas "reducido", donde podremos cambiar algunas características de la fuente con que escribimos. La ventana tendrá una apariencia como la siguiente:

Figura 2. Apariencia de nuestro "bloc de notas"

Vemos que sólo hay un cuadro de texto grande donde escribir, y un menú llamado Formato donde irán todas las opciones para cambiar el formato de la fuente. Dichas opciones serán cambiar el color entre rojo y negro, y cambiar el estilo entre normal, cursiva y/o negrita.

  1. Echa un vistazo a la clase Formatos.java que se proporciona en la plantilla de la sesión. Verás que es un subtipo de Frame, y tiene un constructor vacío, y un método main que crea un objeto de ese tipo y lo muestra (método show). Nos falta completar el constructor para definir la ventana que se mostrará.
  2. Lo primero de todo es importar los paquetes necesarios: el de AWT y el de eventos:
    import java.awt.*;
    import java.awt.event.*;
    
    public class Formatos extends Frame
    {
    	...
  3. Después establecemos el tamaño de la ventana y colocamos los componentes: el menú Formato con sus opciones, y el cuadro de texto:
    public class Formatos extends Frame
    {
    	TextArea txt;
    	boolean negrita = false;
    	boolean cursiva = false;
    
    	public Formatos()
    	{
    		setSize(200, 200);
    	
    		// Menú
    			
    		MenuBar mb = new MenuBar();
    	
    		Menu m = new Menu("Formato");
    	
    		MenuItem mi = new MenuItem("Color Negro");
    		mi.addActionListener(this);
    		m.add(mi);
    		mi = new MenuItem("Color Rojo");
    		mi.addActionListener(this);
    		m.add(mi);
    	
    		CheckboxMenuItem cmi = new CheckboxMenuItem("Negrita");
    		cmi.addItemListener(this);
    		m.add(cmi);
    		cmi = new CheckboxMenuItem("Cursiva");
    		cmi.addItemListener(this);
    		m.add(cmi);
    	
    		mb.add(m);
    	
    		setMenuBar(mb);
    			
    		// Area de texto
    			
    		txt = new TextArea();
    		txt.setFont(new Font("Arial", Font.PLAIN, 16));
    		add(txt, BorderLayout.CENTER);
    	}
    }
    Las variables globales negrita y cursiva de momento no las utilizamos, pero más adelante las emplearemos para guardar en todo momento si están marcadas las opciones de letra negrita o cursiva, respectivamente. En el cuadro de texto establecemos una fuente Arial de 16 puntos, y sin estilo (sin negrita ni cursiva, indicado en la constante Font.PLAIN).
  4. Observa que hay dos tipos de menu items: unos de tipo MenuItem para cambiar el color entre rojo y negro, y otros de tipo CheckboxMenuItem para establecer o no la cursiva y la negrita. Este tipo de items son de los que se marcan y se desmarcan cada vez que se pulsan.

    Observa también que definimos dos tipos de eventos sobre estos items de menú: un evento de tipo ActionListener sobre los MenuItem, y uno de tipo ItemListener para los CheckboxMenuItem. Esto se debe a que, al ser tipos diferentes de items, en AWT responden a dos tipos diferentes de eventos: los primeros son eventos de acción (al pulsar sobre el menú, es como si pulsáramos un botón), y los segundos son de cambio de estado (al pulsar sobre el item, es como si lo marcáramos o desmarcáramos cada vez).

    Por último, observa que los ActionListener e ItemListener que añadimos tienen como parámetro this, es decir, la propia clase debe implementar las interfaces ActionListener e ItemListener:
    public class Formatos extends Frame implements ActionListener, ItemListener
    {
    	...
    1. Para la interfaz ActionListener, debemos definir un método actionPerformed, que será el que se ejecute cuando pulsemos sobre la opción Color Rojo o Color Negro del menú:
      public class Formatos extends Frame implements ActionListener, ItemListener
      {
      	...
      	public Formatos()
      	{
      		...
      	}
      
      	public void actionPerformed(ActionEvent e)
      	{
      		if (e.getActionCommand().equals("Color Negro"))
      		{
      			txt.setForeground(Color.black);
      		} else if (e.getActionCommand().equals("Color Rojo")) {
      			txt.setForeground(Color.red);
      		}
      	}

      ¿Cómo hacemos para distinguir qué opción del menú se ha pulsado en el actionPerformed? ¿Qué método se utiliza para cambiar el color de la fuente de un cuadro de texto? ¿Qué error daría si no hubiésemos puesto la variable txt del área de texto como global?

    2. Para la interfaz ItemListener debemos definir un método itemStateChanged, que será el que se ejecute cuando pulsemos sobre Negrita o Cursiva en el menú:
      public class Formatos extends Frame implements ActionListener, ItemListener
      {
      	...
      	public Formatos()
      	{
      		...
      	}	
      	...
      	public void itemStateChanged(ItemEvent e)
      	{
      		String item = (String)(e.getItem());
      		if (item.equals("Negrita"))
      			negrita = !negrita;
      		else if (item.equals("Cursiva"))
      			cursiva = !cursiva;
      
      		txt.setFont(new Font("Arial", (negrita?Font.BOLD:0) | 
                                                    (cursiva?Font.ITALIC:0), 16));
      	}

      Observa que seguimos manteniendo, como en el constructor, una fuente Arial de 16 puntos. También observa que utilizamos las variables globales negrita y cursiva definidos al principio de la clase. Según si estos flags son verdaderos o falsos, establecemos una combinación de los mismos (con Font.BOLD y Font.ITALIC).

      ¿Cómo hacemos aquí para distinguir qué opción del menú se ha pulsado? ¿Qué método se utiliza para cambiar la fuente de un cuadro de texto? ¿Qué significa el segundo parámetro de dicho método?

  5. Lo último que nos queda por hacer es definir, en el constructor, el evento sobre la ventana (el Frame) para hacer que se cierre y termine el programa cuando pulsemos el botón de cerrar:
    public Formatos()
    {
    	...
    	this.addWindowListener(new WindowAdapter()
    	{
    		public void windowClosing(WindowEvent e)
    		{
    			System.exit(0);
    		}
    	});			
    }
  6. Compila el programa para corregir posibles erratas, y ejecútalo para comprobar que funciona correctamente. Observa que, al cambiar el formato del texto, se cambia para TODO el texto del cuadro, no sólo para lo que vayamos a escribir a continuación.

 

PARA ENTREGAR