Universidad Carlos III de Madrid

Ingeniería de Telecomunicación

Enero-Mayo 2010 / January-May 2010

Interfaces Gráficas de Usuario

Lab Section1. Sesión 4 (laboratorio): Interfaces Gráficas de Usuario (II)

Exercise Section1.1. Introducción a la gestión de eventos

El código que se muestra a continuación construye una interfaz gráfica muy simple, que contiene un botón (javax.swing.JButton) que puede ser pulsado. El resto de los elementos de la interfaz los deberías conocer ya.

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.BorderLayout;

class Simple00GUIEn {

  private static final String FRAME_TITLE = "Simple00GUI";
  private static final String BUTTON_TEXT = "Click me!";

  private void createGUI() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame = new JFrame(FRAME_TITLE);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JPanel contentPane = (JPanel) frame.getContentPane();
    JButton button = new JButton(BUTTON_TEXT);
    contentPane.add(button, BorderLayout.CENTER);
    frame.pack();
    frame.setVisible(true);
  }

  public static void main(String args[]) {
    Simple00GUIEn p = new Simple00GUIEn();
    p.createGUI();
  }
}

          

Descargar este código

  1. Compila y prueba este programa. ¿Qué ocurre al pulsar sobre el botón?

  2. Abre el API de Java y localiza la clase JButton. Este punto ya lo deberías haber realizado por iniciativa propia, esto es un mero recordatorio.

  3. Escribe un programa Simple01GUI, basado en Simple00GUI, en el que al pulsar el botón se muestre un texto por salida estándar. Te será útil el método javax.swing.AbstractButton.addActionListener(ActionListener l), así como el siguiente esqueleto de código.

    import java.awt.event.ActionListener;
    import java.awt.event.ActionEvent;
    
    class ButtonActionListenerEn implements ActionListener {
    
      public void actionPerformed(ActionEvent e) {
    
      }
    }
    
              

    Descargar este código

  4. Escribe un programa Simple02GUI, basado en Simple00GUI, en el que al pulsar el botón se modifique el texto que aparece sobre el mismo.

  5. Escribe un programa Simple03GUI, basado en Simple00GUI, que además contenga un JLabel cuyo texto aparecerá con la primera pulsación del botón.

  6. Escribe un programa TwoButtons que contenga dos botones A y B. Al comenzar el programa, sólamente A estará activado. Al pulsar sobre el botón A, éste se desactivará y será B el que pase a estar activo. El comportamiento de B será simétrico al de A. Aquí tienes un ejemplo: TwoButtons.class.(Nota: Este ejemplo está compuesto de una única clase, pero tu programa no tiene por qué seguir este patrón. Elige la forma de trabajo que más te guste.)

  7. Con respecto al apartado anterior, si lo hiciste con una única clase, repite el ejercicio utilizando varias clases. Si por el contrario utilizaste varias clases, haz las modificaciones necesarias para que sólo haya una clase (un único .class resultante de la compilación).

Soluciones

Las soluciones se pueden ver en los siguientes listados:

Simple01GUIEn

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.BorderLayout;

class Simple01GUIEn {

  private static final String FRAME_TITLE = "Simple00GUIEn";
  private static final String BUTTON_TEXT = "Click me!";

  private void createGUI() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame = new JFrame(FRAME_TITLE);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JPanel contentPane = (JPanel) frame.getContentPane();
    JButton button = new JButton(BUTTON_TEXT);
    button.addActionListener(new ButtonActionListenerEn());
    contentPane.add(button, BorderLayout.CENTER);
    frame.pack();
    frame.setVisible(true);
  }

  public static void main(String args[]) {
    Simple01GUIEn p = new Simple01GUIEn();
    p.createGUI();
  }
}

	  
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

class ButtonActionListenerEn implements ActionListener {

  private static final String MSG = "Hello World!";

  public void actionPerformed(ActionEvent e) {
    System.out.println(MSG);
  }

}

	  

Simple02GUIEn

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import java.awt.GridLayout;
import java.awt.Color;

class Simple02GUIEn {

  private static final String FRAME_TITLE = "Simple02GUIEn";
  private static final String BUTTON_TEXT = "Click me!";
  private static final String LABEL_TEXT = "The button's text change!";

  private void createGUI() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame = new JFrame(FRAME_TITLE);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JPanel contentPane = (JPanel) frame.getContentPane();
    contentPane.setLayout(new GridLayout(2, 1));

    JLabel label = new JLabel(LABEL_TEXT, JLabel.CENTER);
    label.setOpaque(true);
    label.setBackground(Color.GREEN);
    contentPane.add(label);

    JButton button = new JButton(BUTTON_TEXT);
    button.addActionListener(new ButtonActionListenerEn(button));
    contentPane.add(button);

    frame.pack();
    frame.setVisible(true);
  }

  public static void main(String args[]) {
    Simple02GUIEn p = new Simple02GUIEn();
    p.createGUI();
  }
}

	  
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.Color;

class ButtonActionListenerEn implements ActionListener {

  private static final String BUTTON_TEXT = "Click me!";

  private JButton button;

  public ButtonActionListenerEn(JButton button) {
    this.button = button;
  }

  public void actionPerformed(ActionEvent e) {
    System.out.println(Thread.currentThread().getName()
        + ": Changing the button's text.");
    if (button.getText().equals(BUTTON_TEXT)) {
      button.setText("Change me!");
    } else {
      button.setText(BUTTON_TEXT);
    }
  }

}

	  

Simple03GUI

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import java.awt.GridLayout;
import java.awt.Color;

class Simple03GUIEn {

  private static final String FRAME_TITLE = "Simple03GUIEn";
  private static final String BUTTON_TEXT = "Click me!";

  private void createGUI() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame = new JFrame(FRAME_TITLE);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JPanel contentPane = (JPanel) frame.getContentPane();
    contentPane.setLayout(new GridLayout(2, 1));

    JLabel label = new JLabel("", JLabel.CENTER);
    label.setOpaque(true);
    label.setBackground(Color.GREEN);
    label.setVisible(false);
    contentPane.add(label);

    JButton button = new JButton(BUTTON_TEXT);
    button.addActionListener(new ButtonActionListenerEn(label));
    contentPane.add(button);

    frame.pack();
    frame.setVisible(true);
  }

  public static void main(String args[]) {
    Simple03GUIEn p = new Simple03GUIEn();
    p.createGUI();
  }
}

	  
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.Color;

class ButtonActionListenerEn implements ActionListener {

  private static final String LABEL_TEXT = "The label's text appears!";

  private JLabel label;

  public ButtonActionListenerEn(JLabel label) {
    this.label = label;
  }

  public void actionPerformed(ActionEvent e) {
    System.out.println(Thread.currentThread().getName()
        + ": Showing the label's text.");
    label.setText(LABEL_TEXT);
    label.setVisible(true);
  }

}

	  

TwoButtons en una única clase (Descargalo aquí)

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.Object;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TwoButtons implements ActionListener {

  private static final String FRAME_TITLE = "TwoButtons";
  private static final String BUTTON_TEXT = "Click me!";
  private JButton pushed;
  private JButton disabled;

  TwoButtons() {
    pushed = null;
    disabled = null;
    return;
  }

  private void createGUI() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame = new JFrame("TwoButtons");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JPanel contentPane = (JPanel) frame.getContentPane();

    JButton left = new JButton("Click me!");
    JButton rigth = new JButton("Click me!");
    pushed = rigth;
    disabled = left;

    left.addActionListener(this);
    rigth.addActionListener(this);

    contentPane.add(left, "West");
    contentPane.add(rigth, "East");

    frame.pack();
    frame.setVisible(true);
    return;
  }

  private void changeButtonRoles() {
    JButton temp = pushed;
    pushed = disabled;
    disabled = temp;
    return;
  }

  public void enableDisable() {
    pushed.setEnabled(false);
    disabled.setEnabled(true);
    return;
  }

  public void actionPerformed(ActionEvent e) {
    changeButtonRoles();
    enableDisable();
    return;
  }

  public static void main(String[] aStringArray_0) {
    TwoButtons t = new TwoButtons();
    t.createGUI();
    t.enableDisable();
    return;
  }
}

	  

TwoButtons en varias clases(Descargalo aquí)

import java.awt.Component;
import java.lang.Object;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TwoButtons {

  private static final String FRAME_TITLE = "TwoButtons";
  private static final String BUTTON_TEXT = "Click me!";

  private void createGUI() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame = new JFrame("TwoButtons");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JPanel contentPane = (JPanel) frame.getContentPane();

    JButton left = new JButton("Click me!");
    JButton rigth = new JButton("Click me!");
    rigth.setEnabled(false);
    left.setEnabled(true);

    ButtonActionListener buttonListener = new ButtonActionListener(rigth, left);
    left.addActionListener(buttonListener);
    rigth.addActionListener(buttonListener);

    contentPane.add(left, "West");
    contentPane.add(rigth, "East");

    frame.pack();
    frame.setVisible(true);
    return;
  }

  public static void main(String[] aStringArray_0) {
    TwoButtons t = new TwoButtons();
    t.createGUI();
    return;
  }
}

	  

-----------------

import javax.swing.JButton;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ButtonActionListener implements ActionListener {
  private JButton pushed;
  private JButton disabled;

  ButtonActionListener(JButton b1, JButton b2) {
    pushed = b1;
    disabled = b2;
  }

  private void changeButtonRoles() {
    JButton temp = pushed;
    pushed = disabled;
    disabled = temp;
    return;
  }

  public void enableDisable() {
    pushed.setEnabled(false);
    disabled.setEnabled(true);
    return;
  }

  public void actionPerformed(ActionEvent e) {
    changeButtonRoles();
    enableDisable();
    return;
  }
}

	  

Exercise Section1.2. Tres En Raya con contador de tiempo (II)

Para mejorar nuestros conocimientos sobre el modelo de Gestión de Eventos en Java, vamos a proponer el siguiente ejercicio para hacer en casa. Nos basaremos en el código del GUI del Tres En Raya que hemos programado en la sesión de laboratorio y vamos a añadir una nueva funcionalidad que permita controlar el tiempo disponible de cada turno para cada jugador del juego. Es por ello que incorporaremos un nuevo componente que proporciona la librería gráfica de Java (javax.swing) que actua como un "temporizador".

El componente al que hacemos referencia está implementado en la clase javax.swing.Timer y, básicamente, genera eventos del tipo ActionEvent a intervalos determinados constantes y configurables previamente. La forma de gestionar los eventos de este componente se integra en el "Modelo General de Delegación de Gestión de Eventos" de Java que, recordemos, se basa en la existencia de tres clases de objetos:

  • Un conjunto de Fuentes de Eventos (Source Events) que generan eventos de algún tipo como consecuencia de las acciones del usuario al interaccionar con ellos.

  • Un conjunto de Eventos que pueden organizarse en una jerarquía de clases en base a la información almacenada y el origen diverso de los mismos. Por ejemplo, el tipo ActionEvent para acciones sobre un componente; el tipo MouseEvent para movimientos de ratón; el tipo KeyEvent para pulsaciones del teclado, etc…

  • Un conjunto de Escuchadores (Listeners) que reaccionan ante los eventos que generan las anteriores fuentes en base a los distintos tipos de eventos a los que estén subscritos. Para ello, deben implementar la interfaz asociada a cada tipo de evento que quieran gestionar: ActionListener para eventos de tipo ActionEvent; MouseListener para eventos de tipo MouseEvent, etc..

Este modelo tiene muchas ventajas: es entendible, flexible, extensible y robusto. Una buena prueba de esto es lo relativamente sencillo que va a resultar la incorporación de código con lógica adicional de Gestión de Eventos a nuestro GUI del Tres en Raya. Para ello, vamos a hacer la analogía correspondiente de la Gestion de Eventos asociada al nuevo componente que integraremos en nuestra aplicación

  • La Fuente de Eventos será el componente Timer (javax.swing.Timer).

  • El tipo de Evento que se genera y que va a tratarse es del tipo ActionEvent.

  • La clase Escuchadora (TimeListener), tiene que implementar la interfaz asociada al tipo del Evento: ActionListener.

A continuación, se muestra una imagen con la pantalla inicial en la que se muestra la etiqueta con información del contador de tiempo restante del turno del jugador en el panel superior de información (10 segundos en este juego):

El contador de tiempo empieza a descontar segundos hasta que se acaba el tiempo del turno del jugador, en cuyo caso, se muestra la ventana de diálogo con el mensaje de error correspondiente al usuario.

Tras pulsar el botón de Aceptar, se cambia el turno del jugador y se pone el contador de tiempo al valor inicial, tras lo cual, empezará a descontar segundos de nuevo.

Partiendo del código fuente del ejercicio del Tres En Raya de laboratorio que has implementado previamente, realiza las siguientes tareas sobre el mismo para conseguir la incorporación del contador de tiempo:

  • Añade los siguientes atributos a la clase TicTacToe que serán necesarios para integrar el contador de tiempo:

    • Una etiqueta (Jlabel) que contiene el texto con la información el tiempo restante del turno.

    • Una constante entera (final static int) que representa el máximo tiempo para cada turno.

    • Una referencia al objeto Timer (javax.swing.Timer) que será el contador de tiempo.

  • Implementa la clase TimeListenter que actua como clase Escuchadora de los eventos del objeto contador de tiempo.

    • Añade un atributo que sea una referencia a la ventana principal del juego (Jframe) y que se inicializará en el constructor de la clase.

    • Añade otro atributo entero estático que almacenará el número de segundos restantes del turno del jugador.

    • Implementa el método public void actionPerformed(ActionEvent event) que se ejecutará periódicamente por el objeto Timer. Dicho método decrementará el contador de segundos y, en caso de que dicho valor llegue a cero, mostrará el mensaje de error correspondiente al usuario. Por último, cambiará el turno del juego.

  • Crea una instancia del objeto Timer en el constructor de la clase TicTacToe para que el tiempo restante del turno comience a descontar desde ese momento.

    • Como argumento del constructor, pasa una referencia del objeto de la clase Escuchadora TimeListener inicializado con la referencia de la ventana del juego, es decir, con this.

  • Por último, modifica el método que sirve para cambiar el turno del jugador, public void changeTurn(), para que ajuste el valor del contador estático de segundos de la clase Escuchadora (TimeListener) a cero. De esa manera, el contador empezará a descontar el tiempo disponible del nuevo turno del jugador.

Soluciones

El código con la solución del ejercicio se muestra a continuación:

<xi:include></xi:include>
	  
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;

/**
 * This class models the TicTacToe game. It extends from JFrame and builds the
 * board as a bidimentional matrix of Buttons. Event Management is achived in a
 * centralized manner by using a single Event Listener.
 * 
 * @author DIT - UC3M
 * @version 1.0
 */
public class TicTacToeTimer extends JFrame {

  // NON GRAPHIC ATTRIBUTES DECLARATION
  /**
   * Window's title.
   */
  public static final String TITTLE = "3 en raya";

  /**
   * The size of the board (CONSTANT).
   */
  public static final int BOARD_SIZE = 3;

  /**
   * This array stores the number (counter) of figures placed by each player.
   */
  public static int[] placedFigures = { 0, 0 };

  /**
   * Indicates the actual turn's player.
   */
  private int turnPlayer;

  // GRAPHIC ATTRIBUTES DECLARATION

  /**
   * Label that shows the actual turn player.
   */
  private JLabel actualTurn;

  /**
   * Label that shows the image of the actual turn player.
   * 
   */
  private JLabel labelFigure;

  /**
   * Button's bidimensional array that represents the main board of the game.
   */
  private TicTacToeButton[][] board;

  /**
   * El panel que contiene el tablero con los botones (casillas).
   */
  private JPanel panel_board;

  /**
   * El panel que contiene las etiquetas (JLabels).
   */
  private JPanel panel_info;

  // Window's dimension

  /**
   * Window's width (CONSTANT).
   */
  public static final int WIDTH = 500;

  /**
   * Window's height (CONSTANT).
   */
  static final int HEIGHT = 500;

  /**
   * Maximum time for each turn (miliseconds)
   */
  static final int MAX_TURN_TIME = 10000;

  /**
   * Label that holds the turn's time remaining.
   */
  public JLabel labelTimer;

  /**
   * The timer.
   */
  public Timer t;

  /**
   * Class Constructor.
   * 
   * @param title
   *          The game's title..
   */
  public TicTacToeTimer(String title) {

    super(title);

    // Building graphics elements of the game (using an auxiliary method).
    buildGraphicElements();

    // Adding graphic elements to the containers (using an auxiliary method).
    addElementsToContainers();

    // Creating the Event Listener (using an auxiliary method).
    createEventListener();

    // The rest of the initialization process.
    turnPlayer = TicTacToeButton.PLAYER_1;

    // After all, we should pack previously to show.
    pack();

    // Now we create the Timer object
    t = new Timer(1000, new TimerListener(this));

    t.start();

  }

  /**
   * This method creates the graphic elements of the board (labels, buttons,
   * panels, etc...)
   */
  private void buildGraphicElements() {

    // A descriptive label showing the corresponding turn.
    actualTurn = new JLabel("TURNO: [Jugador_" + TicTacToeButton.PLAYER_1
        + "] - ");

    // A label to show the time remaining
    labelTimer = new JLabel("Tiempo restante : ");

    // Starting with Player #1
    labelFigure = new JLabel(TicTacToeButton.images[TicTacToeButton.PLAYER_1]);

    // Creating the main buttons' array to add to the panel later.
    board = new TicTacToeButton[BOARD_SIZE][BOARD_SIZE];
    for (int f = 0; f < BOARD_SIZE; f++) {
      for (int c = 0; c < BOARD_SIZE; c++) {
        board[f][c] = new TicTacToeButton();
      }
    }

  }

  /**
   * This auxiliary method adds elements to its containers respectively.
   */
  private void addElementsToContainers() {

    // Creating the panels to add elements
    // The main board panel
    panel_board = new JPanel();
    panel_board.setLayout(new GridLayout(BOARD_SIZE, BOARD_SIZE));

    // adding buttons to the container
    for (int f = 0; f < BOARD_SIZE; f++) {
      for (int c = 0; c < BOARD_SIZE; c++) {
        panel_board.add(board[f][c]);
      }
    }

    // The info's panel ("Flowlayout" as default)
    panel_info = new JPanel();
    // Adding the actual turn player info and the associated image.
    panel_info.add(actualTurn);
    panel_info.add(labelFigure);
    // Adding the label with the time remaining
    panel_info.add(labelTimer);

    // Finally, adding the previous panels to the main panel container
    // ("BorderLayout" as default)
    // We add the info's panel to the North and the board's panel to the South.
    // But first of all, we have to obtain a reference to the Main's Window
    // panel container.
    Container content = getContentPane();
    content.add(panel_info, BorderLayout.NORTH);
    content.add(panel_board, BorderLayout.CENTER);

  }

  /**
   * This auxiliary method configures the Event Listener for the graphic
   * elements (Buttons) .
   */
  private void createEventListener() {

    // First of all, Create the Listener that manages the events of the buttons
    TicTacToeButtonListener butListen = new TicTacToeButtonListener(this);

    // Finally, registering the Listener on each element source of events, in
    // this case,
    // each button.
    for (int f = 0; f < BOARD_SIZE; f++)
      for (int c = 0; c < BOARD_SIZE; c++)
        board[f][c].addActionListener(butListen);
  }

  /**
   * This auxiliary method changes the turn of the player.
   */
  public void changeTurn() {

    // Reset the second's counter
    TimerListener.sec_count = 0;

    if (turnPlayer == TicTacToeButton.PLAYER_1) {
      turnPlayer = TicTacToeButton.PLAYER_2;
      actualTurn.setText("TURNO Jugador_" + TicTacToeButton.PLAYER_2);
      labelFigure.setIcon(TicTacToeButton.images[TicTacToeButton.PLAYER_2]);

    } else {
      turnPlayer = TicTacToeButton.PLAYER_1;
      actualTurn.setText("TURNO Jugador_" + TicTacToeButton.PLAYER_1);
      labelFigure.setIcon(TicTacToeButton.images[TicTacToeButton.PLAYER_1]);
    }
  }

  /**
   * This auxiliary method test if the owner has won the game.
   */
  private boolean hasWon(int player) {

    int countFigures = 0;
    // Test if there's a line of figures of the player
    // Horizontal lines
    for (int i = 0; i < BOARD_SIZE; i++) {
      countFigures = 0;
      for (int j = 0; j < BOARD_SIZE; j++) {
        if (board[i][j].getPlayer() == player) {
          countFigures++;
          if (countFigures == BOARD_SIZE) {
            return true;
          }
        }
      }
    }

    // Vertical lines
    for (int i = 0; i < BOARD_SIZE; i++) {
      countFigures = 0;
      for (int j = 0; j < BOARD_SIZE; j++) {
        if (board[j][i].getPlayer() == player) {
          countFigures++;
          if (countFigures == BOARD_SIZE) {
            return true;
          }
        }
      }
    }

    // First Diagonal
    countFigures = 0;
    for (int i = 0; i < BOARD_SIZE; i++) {

      if (board[i][i].getPlayer() == player) {
        countFigures++;
        if (countFigures == BOARD_SIZE) {
          return true;
        }
      }
    }

    countFigures = 0;
    // First Diagonal
    for (int i = 0; i < BOARD_SIZE; i++) {
      if (board[BOARD_SIZE - (i + 1)][i].getPlayer() == player) {
        countFigures++;
        if (countFigures == BOARD_SIZE) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * The main method of the class
   * 
   * @param args
   *          The parameters' array.
   */
  public static void main(String[] args) {

    // Building a TicTacToe's instance
    TicTacToeTimer game = new TicTacToeTimer(TicTacToeTimer.TITTLE);

    // Registering a Listener to react to the window closing event
    // We use an anonymous class to implement the interface's method.
    game.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });

    // Finally, we set the size of the window and we show it
    game.setSize(WIDTH, HEIGHT);
    game.setVisible(true);

  }

  /**
   * This class implements de Listener of events that will be produced by the
   * buttons of the main board. The Events will be thrown as ActionEvents and,
   * consequentlly, it has to implement the corresponding method of the
   * interface ActionListener.
   */
  class TicTacToeButtonListener implements ActionListener {

    /**
     * We need a reference to the frame to access the elements we want to
     * change.
     */
    private JFrame miFrame;

    /**
     * Class constructor.
     * 
     * @param frame
     *          The frame that we need to store so we can acces the elements to
     *          modify later.
     */
    public TicTacToeButtonListener(JFrame frame) {

      this.miFrame = frame;

    }

    /**
     * This is the method of the ActionListener interface that has to be
     * overwritten.
     * 
     * @param event
     *          The ActionEvent's event that is thrown when the button is
     *          clicked.
     */
    public void actionPerformed(ActionEvent event) {
      // First of all, we have to obtain the reference of the button that has
      // been pressed.
      TicTacToeButton clickedButton = (TicTacToeButton) event.getSource();

      // If the player has still figures left to put (the size of the board)
      if (placedFigures[turnPlayer] < BOARD_SIZE) {

        // We check if the square is free or not
        if (clickedButton.isFree()) {
          // We update the state of the square, that is, the button.
          clickedButton.setPlayer(turnPlayer);

          // Incrementing the counter of placed figures of the player
          ((TicTacToeTimer) miFrame).placedFigures[turnPlayer]++;

          // Check if we have won the game
          if (placedFigures[turnPlayer] == BOARD_SIZE) {
            // Check if the player has won the game
            if (hasWon(turnPlayer)) {
              // ¡¡ GREETINGS, THE PLAYER HAS WON THE GAME !!
              javax.swing.JOptionPane.showMessageDialog(miFrame,
                  "ENHORABUENA, JUGADOR_" + turnPlayer + ","
                      + "HAS GANADO EL JUEGO !!");
              System.exit(0);
            }

          }
          // And, finally, we change the player's turn
          changeTurn();
        } else {
          // We show a Message Dialog with an error message.
          javax.swing.JOptionPane.showMessageDialog(miFrame,
              "No se puede poner ahi el simbolo.");
        }

      } else {
        // We have no figures left, we must take out one to move it.
        actualTurn.setText("HAY QUE MOVER UN SIMBOLO!");
        // We have to check whether we are the correct player to take it out
        if (clickedButton.getPlayer() == turnPlayer) {
          // in that case, the square becomes free
          clickedButton.setPlayer(TicTacToeButton.FREE);

          // Decrementing the counter of placed figures
          ((TicTacToeTimer) miFrame).placedFigures[turnPlayer]--;
        } else {
          // We are not the correct player so we alert.
          javax.swing.JOptionPane.showMessageDialog(miFrame,
              "No se puede poner ahi el simbolo.");
        }
      }
    }
  }

}

class TimerListener implements ActionListener {

  /**
   * We need a reference to the frame to access the elements we want to change.
   */
  private JFrame miFrame;

  /**
   * Class constructor.
   * 
   * @param frame
   *          The frame that we need to store so we can acces the elements to
   *          modify later.
   */
  public TimerListener(JFrame frame) {

    this.miFrame = frame;

  }

  // The second's counter
  public static int sec_count = 0;

  /**
   * This is the method of the ActionListener interface that has to be
   * overwritten.
   * 
   * @param event
   *          The ActionEvent's event that is thrown when the time period has
   *          been reached.
   */
  public void actionPerformed(ActionEvent event) {

    int temp = TicTacToeTimer.MAX_TURN_TIME - sec_count * 1000;

    if (temp < 0) {
      sec_count = 0;
      javax.swing.JOptionPane.showMessageDialog(miFrame,
          "Se ha acabado el tiempo !!");

      ((TicTacToeTimer) miFrame).changeTurn();

    } else {

      ((TicTacToeTimer) miFrame).labelTimer.setText("Tiempo restante : " + temp
          / 1000);

      sec_count++;
    }

  }

}
	  

Homework Section2. Actividades para casa