Universidad Carlos III de Madrid

Ingeniería de Telecomunicación

Enero-Mayo 2010 / January-May 2010

Graphical User Interfaces

Lab Section1.  Session 2 (lab): Graphical User Interfaces

Exercise Section1.1.  Passive elements in GUIs

Probably one of the simplest graphic component is a text label (javax.swing.JLabel). The following code shows a simple GUI example: a window with a text label:

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;

class HelloWorldGUI {

  public static void main(String args[]) {
    JFrame frame = new JFrame("HelloWorldGUI window title");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel contentPane = (JPanel) frame.getContentPane();
    JLabel label = new JLabel("Hello World!");
    contentPane.add(label);

    frame.setSize(100, 50);
    frame.setVisible(true);
  }
}

          

Download this code

  1. Compile and test this program.

  2. Enumerate, on a sheet of paper, the names of the classes of the objects that are created throughout the execution of the program.

  3. Open the Java API and find those classes. Which package can you find them in?

  4. Enumerate the methods that are invoked throughout the execution of the program and the classes to which they belong to (according to their hierarchical structure). Do not forget to enumerate the constructors.

  5. Try to explain, writing on a sheet of paper, what every line of code does in the execution of the code. When you have finished all the exercises of this session, return to this section and review your notes to see if you can add something new.

  6. Implement a HelloWorldGUIBig program similar to HelloWorldGUI where window's size is 4 times bigger.

  7. Write a HelloWorldGUIColor program similar to HelloWorldGUI where the background colour of the label is blue.

Solutions

Solutions for problems 1-5 are not shown here. Ask your teacher.

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;

class HelloWorldGUI {

  public static void main(String args[]) {
    JFrame frame = new JFrame("HelloWorldGUI window title");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel contentPane = (JPanel) frame.getContentPane();
    JLabel label = new JLabel("Hello World!");
    contentPane.add(label);

    frame.setSize(100, 50);
    frame.setVisible(true);
  }
}

	  

The solution of problem 6 is:

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;

class HelloWorldGUIBig {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;

    frame = new JFrame("HelloWorldGUIBig");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    contentPane = (JPanel) frame.getContentPane();
    label = new JLabel("Hello World!");
    contentPane.add(label);

    frame.setSize(400, 200);
    frame.setVisible(true);
  }
}

	  

Problem 7 can be solved with the following code:

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

class HelloWorldGUIColor {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;

    frame = new JFrame("HelloWorldGUIColor");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    contentPane = (JPanel) frame.getContentPane();
    label = new JLabel("Hello World!");
    label.setOpaque(true);
    label.setBackground(Color.BLUE);
    contentPane.add(label);

    frame.setSize(100, 50);
    frame.setVisible(true);
  }
}

	  

Exercise Section1.2.  Introduction to event management

The following code is a simple graphic interface which contains a javax.swing.JButton button that can be clicked. The rest of the elements of the interface should be well known by you.

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

          

Download this code

  1. Compile and execute this program. What happens when the button is pressed?

  2. Open the Java API and localize the JButton class (this is a good habit that you should always do when programming).

  3. Implement a program, Simple01GUIEn, based on Simple00GUIEn, in which a text message is printed to console when the button is pressed. You will find useful the javax.swing.AbstractButton.addActionListener(ActionListener l) method and the following code shell:

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

    Download this code

  4. Implement a program Simple02GUIEn, based on Simple00GUIEn, in which the program modify its text when the user clicks the button.

  5. Implement a program Simple03GUIEn, based on Simple00GUIEn, which also contains a JLabel which text appears after the first button press.

  6. Write a TwoButtons program that contains two buttons, A and B. When the program starts, only the A button must be active. When the A button is clicked, it must be disabled and B must turn into active. The behaviour of B button must be simmetric to A. Here you can find an example: TwoButtons.class. (Note: This example is composed of a single class, but you are not forced to follow the same pattern. Choose the more suitable way for you).

  7. Regarding the previous section, if you made the program with a single class, repeat the exercise using several classes. If you implemented several classes, make the necessary changes in order to have a single class (that is, only a .class file).

Solutions

Solutions are included in the following listings:

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

}

	  

Simple03GUIEn

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 in a single class(Download it)

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 in several classes(Download it

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.3.  Tic Tac Toe basic (I)

During this exercise, we are going to practice with the implementation of a complete example of a Graphic User Interface (GUI) that integrates the corresponding Event Management associated to the graphic elements that are composed of.

To do so, we will base on the Tic Tac Toe famous game. According to the Wikipedia, the Tic-tac-toe (also spelled tick tack toe, and alternatively called noughts and crosses, Xs and Os, and many other names) is a pencil-and-paper game for two players, O and X, who take turns marking the spaces in a 3×3 grid, usually X going first. The player who succeeds in placing three of his/her marks in a horizontal, vertical or diagonal row wins the game.

To simulate the game board, we will use the following graphic elements that belong to the javax.Swing package:

  • The cells of the board are simulated with graphic buttons (javax.swing.JButton) with an associated image (javax.swing.ImageIcon).

    • If the space is free, without having being marked, the button will have no image associated to it.

    • Marking a cell is simulated by associating an image to the button. The image is binded to each player's turn respectively: O and X

      Images are shown next so you can download them:

      Note

      When initialising an ImageIcon object with the path to the button image, make sure that such path is correct so that it can be shown to the user.

  • The board will be modelled as a javax.swing.Jpanel container element, whose associated layout will be java.awt.GridLayout (3x3). Additionally, a 3x3 bidimensional array will be used for storing the corresponding buttons associated to each of the cells of the board.

    • The 3x3 bidimensional array will be filled in with the previous buttons.

The following image shows the main window of the game, which consists of the following panels (JPanel): an upper panel (panel_info) that contains information about the turn of the current player; a central panel (panel_board) that initially contains the button's array without any associated image, that is, the initial empty game board that is ready to start playing:

As the turns of players turns succeed, each player “marks the space" by clicking the corresponding button. To simulate the space mark, the button image will be changed according to the symbol of each player as shown at the next figure:

In case of some space could not be marked (for example, if there are no symbols left for the player or if the player's turn is not correct), an error message should be displayed.

Finally, if a player wins the game, a congratulations message will be displayed as shown in the next figure:

The TicTacToeButton class.

Let's implement the TicTacToeButton class that represents the space or square each player can mark. To simulate the mark, the button will update its associated image according to whether it is free (it has no image) or marked (with the associated image of the corresponding player). To achieve that, the class has the following attributes:

  • An integer attribute (turnPlayer) to store the player's turn and it represents the state of the space (square).

  • Three integer constants (FREE, PLAYER_1, PLAYER_2) that indicate the possible states associated to the buttons.

  • An image array (javax.swing.ImageIcon[]) that stores the respective players' symbols: O y X

Download the class skeleton from the following link TicTacToeButton.java and implement the following methods:

  • The class constructor public TicTacToeButton() that initializes the button and let it in free state (FREE), that is, with no player's turn and, therefore, without any associated image to it.

  • The public void setPlayer(int newPlayer) method that updates the button's state with the turn of the new player and its corresponding image.

Test the operation by executing the main class method and passing it, as an argument, any of the three possible button's states (FREE(-1), PLAYER_1(0), PLAYER_2(1)) to see whether the result is correct or not.

The TicTacToe class.

In this section, we are not going to program anything, we just intend to observ how the Tic Tac Toe main board Graphic Interface has been built. Download the TicTacToe class skeleton from the following link TicTacToe.java and look at the following methods that allow building the Graphic Interface.

Note

The private void createEventListener() method that implements the Event Management configuration has been left empty until the next section.

  • The private void buildGraphicElements() method initializes all necessary graphic elements to build the Graphic Interface (labels and array of buttons) and initializes them to free state (FREE), as has been previously mentioned in the text.

  • The private void addElementsToContainers() method adds the previous graphic elements to their corresponding panels (information and board). Finally, it adds those panels to the window main container.

To make the main GUI window visible with the initial state of the game, execute the main methdod of the class. This method just creates, initializes and makes it visible by invoquing those previous methods.

The TicTacToeButtonListener class.

In this chapter we are going to "revive" the game so that it will be able to interact with the user according to the rules of the game. All this is achieved by the implementation of the GUI “Event Management” that involves, normally, the following tasks:

  1. To decide what kind of graphic components of the Graphic Interface constitute the "Event Source", that is, which components are going to generate events that we will want to treat later. In our case, such graphic components will be the board buttons (JButton) that represent the board spaces or squares.

  2. To decide what type of events, from all those that the components are able to generate, are going to be treated later by the corresponding listener or listeners. In this case, all events that we will treat are of ActionEvent type, related to the user clicking the button.

  3. To define the number of Listener classes that are going to react to the previous events. The number of classes is variable, it can be a unique class or serveral depending on the strategy of design for the Event Management. In this case, we will implement a unique listener class, TicTacToeButtonListener, that will be the responsible for the management of every ActionEvent event that is generated when a button (TicTacToeButton) is clicked. This is achieved by implementing the respective interface (interface) that is associated with that type of event, in our case, the ActionListener interface.

  4. For each graphic element that will act as an "Event Source" and wants to be treated later by one or several Listener classes, it is necessary previously register it (or them) at the compoment. In our case, once an instance object of our Listener class (TicTacToeButtonListener) has been created, it has to be registered on every board button by calling to the public void addActionListener(ActionListener l) method.

  5. Finally, to implement the Event Listener class, TicTacToeButtonListener, that is, implement both the constructor and all methods of the interface or interfaces that have to be implemented according to the previous selected strategy (point 2). The real event management associated to the GUI is realized in these methods.

Regarding the three first points (1. 2. and 3.), everything can be summed up in the fact that the only graphic components that are going to act as event sources will be the board buttons (JButton). Regarding the distinct types of events that can be treated, only events of ActionEvent type, that are generated when a button is clicked, will be managed. It has been decided to create only one listener class, TicTacToeButtonListener, that will take charge of the management of ActionEvent type events by implemmenting the ActionListener associated interface and, therefore, the following single method of it: public void actionPerformed(ActionEvent event).

In this section, we will implement the code of the two last points (4. and 5.).

  • The point 4. is implemented with the private void createEventListener() method of the TicTacToe class. It is in charge of the creation and the subsequent registry of the listener for each graphic element that generates events, that is, for each button of the board.

    Implement the code of the method in the TicTacToe skeleton class according to what has been specified in the text.

  • The point 5. force us to implement the public void actionPerformed(ActionEvent event) in the TicTacToeButtonListener listener class. In this method is where the actual GUI Event Management is realized. The generated event is passed as the event parameter to the method.

The flow diagram associated to the public void actionPerformed(ActionEvent event) method is described as follows. It is specific for a "PLAYER_X" player and it represents the program flow that it is realized when such player clicks the button that corresponds to the space (s)he wants to mark. In the flow diagram, basically three conditions are checked before verifying whether the player has won the game:

  • First, it is checked whether the player has enough free symbols to mark the space (remember, the player owns only three symbols to use).

    • In case of affirmative answer, it is checked whether the space is free (FREE) or not and, in that case, a message error should be displayed to the player.

    • In case of negative answer, it is checked whether the actual turn belongs to the "PLAYER_X" player or not and, in that case, a message error should be displayed to the player.

Note

To show a Dialog's Window with an error message, you can use the static showMessageDialog method from the javax.swing.JOptionPane class.

Solutions

The exercise solution's code is shown next:

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;

/**
 * This class implements a square of the board as a button. The button has to
 * mantain the correct state (the owner's player) and has to change it according
 * to the game's rules.
 */
public class TicTacToeButton extends JButton {

  // State variables
  /**
   * Indicates if square is free, it can be occupied by the other player.
   */
  public static final int FREE = -1;

  /**
   * The square is occupied by player #1
   */
  public static final int PLAYER_1 = 0;

  /**
   * The square is occupied by player #2
   */
  public static final int PLAYER_2 = 1;

  /**
   * This array stores the buttons' images.
   */
  public static Icon[] images;

  // Static constructor
  static {
    // Creating the images' array to make them accesible.
    images = new ImageIcon[] { new ImageIcon("img/x.jpg"),
        new ImageIcon("img/o.jpg") };
  }

  /**
   * The state of the button, that is, the owner's player.
   */
  private int ownerPlayer;

  /**
   * Default constructor.
   */
  public TicTacToeButton() {
    // First of all, always we call to the base class constructor (always)
    super();
    // Adjusting the state to FREE
    setPlayer(FREE);
  }

  /**
   * Return whether the button's state is free or not.
   * 
   * @return true if it is free, or false if it is owned by a player.
   */
  public boolean isFree() {
    return ownerPlayer == FREE;
  }

  /**
   * This method updates the state of the button with the new player that is
   * passed as parameter.
   * 
   * @param newPlayer
   *          The new player that has "marked" the square, that is, that has
   *          clicked the botton.
   */
  public void setPlayer(int newPlayer) {
    // Update the new player
    ownerPlayer = newPlayer;
    // Changing the button's icon according to the state.
    if (ownerPlayer == FREE) {
      // calling the super class method to delete the button's image.
      setIcon(null);
    } else {
      // calling the super class method to update the button's image.
      setIcon(images[ownerPlayer]);
    }

  }

  /**
   * Returns the button's state, that is, the owner of the square(button).
   * 
   * @return The button's owner player.
   */
  public int getPlayer() {
    return ownerPlayer;
  }

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

    int player = 0;

    // Create a window. Use JFrame since this window will include
    // lightweight components.
    JFrame frame = new JFrame("Demo TicTacToeButton");

    JLabel lTitle = new JLabel();

    // Create a button.
    TicTacToeButton bTic = new TicTacToeButton();

    // Adjusting the state according to the argument
    if (args.length == 1) {
      player = Integer.parseInt(args[0]);
      if (player == 0 || player == 1 || player == -1)
        bTic.setPlayer(Integer.parseInt(args[0]));
      else {
        javax.swing.JOptionPane.showMessageDialog(frame,
            "Valor Argumento incorrecto.Los unicos posibles son -1, 0 o 1.");
        System.exit(0);
      }

    }

    switch (player) {
    case -1:
      lTitle.setText("Casilla LIBRE (FREE) -> Estado -1");
      break;
    case 0:
      lTitle.setText("Casilla JUGADOR_1 (PLAYER_1) -> Estado 0");
      break;
    case 1:
      lTitle.setText("Casilla JUGADOR_2 (PLAYER_2) -> Estado 1");
      break;
    default:
      break;
    }

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

    // Finally, we set the size of the window and we show it
    frame.getContentPane().add(bTic, BorderLayout.CENTER);
    frame.getContentPane().add(lTitle, BorderLayout.NORTH);

    frame.setSize(300, 300);
    frame.setVisible(true);

  }

}

	  
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 TicTacToe 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 = 400;

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

  /**
   * Class Constructor.
   * 
   * @param title
   *          The game's title..
   */
  public TicTacToe(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();

  }

  /**
   * 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
        + "] - ");

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

    // 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.
   */
  private void changeTurn() {
    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
    TicTacToe game = new TicTacToe(TicTacToe.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
          ((TicTacToe) 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
          ((TicTacToe) 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.");
        }
      }
    }
  }

}

	  

Homework Section2.  Homework

Exercise Section2.1. Practice with the Window Manager

A Window Manager is a program that controlls the windows in a graphic environment. Window Managers allow the user to manage windows without communicating with the programs that create them. This way, a Window Manager allows to perform the following tasks with windows on a graphic environment: to create and destroy windows, to move and resize windows, to change the active window (focus selection) and to hide and show hidden windows.

This is why programs that open windows, have to both communicate with the user and with the Window Manager; that is, a graphic program will have to provide the following communication interfaces:

  • An interface for the user to communicate with the program. It can be either the standard input and/or any graphic component (a button, etc.) that is located at the window.

  • An interface for the program to comnunicate with the user. It can be either the standard output and/or text labels and drawings that may be depicted at the window.

  • An interface for the window to communicate with the Window Manager and vice versa. For example, when the user communicates with the Window Manager asking for closing the window (by clicking the cross button of a window), the Window Manager passes the message to the window and this one passes it to the program that controls it to finish its execution.

Most graphic environments permit to execute different Window Managers on them, each one with distinct ways of working, some of them more comfortable than others, some of them nicer than others. Windows Operating System uses a single Window Manager by default. Probably, it is the only Window Manager you know, but there are a lot of them (hundreds).

Usually, Window Managers communicate with the user through a serie of gadgets added to windows (for example, a border, a title bar and some buttons). These gadgets, besides some key combinations (e.g., Alt-Tab), permit the user to send commands to the Window Manager about what to do with the window (maximize it hide it, etc.)

JFrame class has a set of methods that allows the window to communicate with the Window Manager and vice versa.

  • setVisible(boolean b)

    By default, new windows are not visible on the screen. Use this method to make a window visible. Do not do it right after its creation because probably you may want to configure some details before making it visible.

  • setLocation(int x, int y)

    By default, new windows are placed at position (0,0); that is the upper left corner of the screen. With this method, you can choose the position where the window will appear. X axis grows rigthwards and Y axis grows downwards.

  • setUndecorated(boolean b)

    By default, the Window Manager adds its own decoration to the windows you create (normally, a tittle bar and some buttons to manage the window). If you do not want it to do it, use this method. Note: It is illegal to change the window decoration once it is visible.

    Note

    Some Window Managers, like Windows XP one, communicate with the user mainly with these decorations; if you remove them, it could be difficult for the user to manage the window.

  • setExtendedEstate(int state)

    Windows can have different states for the Window Manager. They can be visible, iconified (hidden), maximized, etc. Use this method to modify such state, but note that not all systems admit all states.

  • setDefaultLookAndFeelDecorated(boolean b)

    By default, new windows are decorated according to the preferences of the Window Manager. This decoration can be overwritten by the "Look and Feel" we have choosen previously.

  • setDefaultCloseOperation(int operation)

    When a user asks the Window Manager to close a window, the Window Manager asks the window to execute a certain operation, normally to kill the process. By the use of this method, we can configure the behaviour of our window so that it performs some other operations, such as ignore the message or to make some alternative operation (e.g., showing a closing-confirmation dialogue).

To do this exercise, make the following tasks:

  1. Have a look to the Java API and try to understand the description of each of the previous methods.

  2. Write a HelloWorldGUIDeaf program similar to HelloWorldGUI that ignores the Window Manager's closing message. Test it. How can the window be closed now?

  3. Write a HelloWorldGUIUndecorated program similar to HelloWorldGUI that do not use any Window Manager decoration.

  4. Write a HelloWorldGUIDecoratedprogram similar to HelloWorldGUI that uses the Java's default "Look and Feel" instead of the Window Manager one.

Solutions

Solutions are included in the following listings:

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;

class HelloWorldGUIDeaf {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;

    frame = new JFrame("HelloWorldGUIDeaf");
    frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

    contentPane = (JPanel) frame.getContentPane();
    label = new JLabel("Hello World!");
    contentPane.add(label);

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

	  
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;

class HelloWorldGUIUndecorated {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;

    frame = new JFrame("HelloWorldGUIUndecorated");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    contentPane = (JPanel) frame.getContentPane();
    label = new JLabel("Hello World!");
    contentPane.add(label);

    frame.setSize(100, 50);
    frame.setUndecorated(true);
    frame.setVisible(true);
  }
}
	  
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;

class HelloWorldGUIDecorated {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;

    JFrame.setDefaultLookAndFeelDecorated(true);
    frame = new JFrame("HelloWorldGUIDecorated");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    contentPane = (JPanel) frame.getContentPane();
    label = new JLabel("Hello World!");
    contentPane.add(label);

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

Exercise Section2.2.  Introduction to different Layouts

Usually, graphic interfaces of a program have a great number of different components . This is why it is necessary to have a method to distribute them throughout the window that contains them.

Java Layout Managers cover this need. By means of standard positioning policies, some generic guidelines are established so that the LayoutManager carries these policies out accurately.

A JPanel has a positioning policy associated to it, but can be customized according to programmer's needs. In this exercise, we will work with a kind of Layout called BorderLayout ( "border" means "frontier" ) which is configured as the default border of the contentPane of a JFrame

  1. Implement a first version of a program called CompassSimple that should be like HelloWorldGUIColor but, in this case, it has to set the label with the background color to black and the foreground color to white. The window should be 400x200.

  2. Modify the program CompassSimple so that it shows 4 labels:

    • Text: "North", background colour: black, foreground colour: white

    • Text: "South", background colour: white, foreground colour: black

    • Text: "East", background colour: blue, foreground colour: red

    • Text: "West", background colour: red, foreground colour: blue

    Every label should be placed at the window, with a BorderLayout positioning policy, according to the guidelines that indicate its text ("East" label at the left of the panel, etc.). Do not make any aesthetic considerations about the result. It could be useful to go to the Java API, which provides information about attributes and methods of the following classes:

    java.awt.Container //Especially the different variations of add() method
    java.awt.BorderLayout //Especially the static attributes
    
              

    Using the window's handler, modify the size of the window and see how all labels are automatically positioned in the correct place.

  3. When you create a label, which horizontal alignment has its text by default? And which vertical alignment?

Solution

Solutions are included in the following listings:

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Frame;

class CompassSimple {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;

    frame = new JFrame("CompassSimple");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    contentPane = (JPanel) frame.getContentPane();

    label = new JLabel("North");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    contentPane.add(label, BorderLayout.NORTH);

    label = new JLabel("South");
    label.setOpaque(true);
    label.setBackground(Color.WHITE);
    label.setForeground(Color.BLACK);
    contentPane.add(label, BorderLayout.SOUTH);

    label = new JLabel("East");
    label.setOpaque(true);
    label.setBackground(Color.BLUE);
    label.setForeground(Color.RED);
    contentPane.add(label, BorderLayout.EAST);

    label = new JLabel("West");
    label.setOpaque(true);
    label.setBackground(Color.RED);
    label.setForeground(Color.BLUE);
    contentPane.add(label, BorderLayout.WEST);

    if (frame.getToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH))
      frame.setExtendedState(Frame.MAXIMIZED_BOTH);
    else
      frame.setSize(frame.getToolkit().getScreenSize());
    frame.setVisible(true);
  }
}
	  

Exercise Section2.3. Practice with diferent Layout Managers

We will start from the introduction exercise about the different types of Layouts. To do so, perform the following tasks:

  1. Implement a CompassBetter program that centers the text of every label, set the background colour to black and the foreground colour to white.

  2. You know that labels can contain text; however they can also contain images. Write a CompassMuchBetter program similar to CompassBetter but with a label with this image, in centered alignment. Be careful with the path of the image.

    The result should be something like this (border colours can change in your system):

  3. The JFrame.pack() method calculates the minimal window size so that all components contained in it can be placed in a reasonable manner. It calls JFrame.setSize() method with that size. Implement a CompassMuchBetterSmall program similar to CompassMuchBetter but, in this case, the window size should be the smallest necessary to be shown.

  4. Write a HelloWorldGUICentered program similar to HelloWorldGUI that shows its window centered and has its size automatically calculated.

    Suggestion

    Screen size can be obtained using JFrame.getToolkit().getScreenSize() method and window size can also be calculated using JFrame.getWidth() and JFrame.getHeight().

  5. Implements a centerAndShowFrame(JFrame frame, double xScale, double yScale) method that centers the window at the screen, resizes it to the minimal size scaled by xScale and yScale values and finally, makes it visible. You can encapsulate this method in a FrameUtilities class to reuse it in the rest of the programs you make in the future.

Solutions

The solutions are included in the following listings:

BorderTest

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.border.TitledBorder;
import javax.swing.border.LineBorder;

class BorderTest {
  private static final int NUM_ROWS = 20;

  private static boolean isEven(int i) {
    if (i % 2 == 0)
      return true;
    else
      return false;
  }

  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;

    JFrame.setDefaultLookAndFeelDecorated(true);
    frame = new JFrame("BorderTest");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    contentPane = (JPanel) frame.getContentPane();
    contentPane.setLayout(new GridLayout(NUM_ROWS, 1));
    contentPane.setBorder(new TitledBorder(new LineBorder(Color.BLACK, 1),
        "contentPane(GridLayout)"));

    for (int i = 0; i < NUM_ROWS; i++) {
      label = new JLabel("label " + i);
      label.setOpaque(true);
      if (isEven(i)) {
        label.setBackground(Color.WHITE);
        label.setForeground(Color.BLACK);
      } else {
        label.setBackground(Color.BLACK);
        label.setForeground(Color.WHITE);
      }
      label.setHorizontalAlignment(JLabel.CENTER);
      label.setVerticalAlignment(JLabel.CENTER);
      contentPane.add(label);
    }

    FrameUtilities.centerAndShowFrame(frame, 3.0, 1.2);
  }
}

	  

CompassBetter

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Frame;

class CompassBetter {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;

    frame = new JFrame("CompassBetter");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    contentPane = (JPanel) frame.getContentPane();

    label = new JLabel("North");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.NORTH);

    label = new JLabel("South");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.SOUTH);

    label = new JLabel("East");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.EAST);

    label = new JLabel("West");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.WEST);

    if (frame.getToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH))
      frame.setExtendedState(Frame.MAXIMIZED_BOTH);
    else
      frame.setSize(frame.getToolkit().getScreenSize());
    frame.setVisible(true);
  }
}

	  

CompassMuchBetter

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Frame;
import javax.swing.ImageIcon;

class CompassMuchBetter {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;
    ImageIcon arrow;

    frame = new JFrame("CompassMuchBetter");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    contentPane = (JPanel) frame.getContentPane();

    label = new JLabel("North");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.NORTH);

    label = new JLabel("South");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.SOUTH);

    label = new JLabel("East");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.EAST);

    label = new JLabel("West");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.WEST);

    arrow = new ImageIcon("./arrow.jpg");
    label = new JLabel(arrow);
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.CENTER);

    frame.setSize(200, 200);
    frame.setVisible(true);
  }
}

	  

CompassMuchBetterCentered

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Frame;
import javax.swing.ImageIcon;

class CompassMuchBetterCentered {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;
    ImageIcon arrow;

    frame = new JFrame("CompassMuchBetter");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    contentPane = (JPanel) frame.getContentPane();

    label = new JLabel("North");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.NORTH);

    label = new JLabel("South");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.SOUTH);

    label = new JLabel("East");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.EAST);

    label = new JLabel("West");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.WEST);

    arrow = new ImageIcon("./arrow.jpg");
    label = new JLabel(arrow);
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.CENTER);

    FrameUtilities.centerAndShowFrame(frame, 1.2, 1.2);
  }
}

	  

CompassMuchBetterSmall

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Frame;
import javax.swing.ImageIcon;

class CompassMuchBetterSmall {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;
    ImageIcon arrow;

    frame = new JFrame("CompassMuchBetter");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    contentPane = (JPanel) frame.getContentPane();

    label = new JLabel("North");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.NORTH);

    label = new JLabel("South");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.SOUTH);

    label = new JLabel("East");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.EAST);

    label = new JLabel("West");
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setForeground(Color.WHITE);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.WEST);

    arrow = new ImageIcon("./arrow.jpg");
    label = new JLabel(arrow);
    label.setOpaque(true);
    label.setBackground(Color.BLACK);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setVerticalAlignment(JLabel.CENTER);
    contentPane.add(label, BorderLayout.CENTER);

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

	  

HelloWorldGUICentered

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Dimension;

class HelloWorldGUICentered {
  public static void main(String args[]) {
    JFrame frame;
    JPanel contentPane;
    JLabel label;
    Dimension screenDim;
    int screenWidth, screenHeight;
    int frameWidth, frameHeight;

    frame = new JFrame("HelloWorldGUIiCentered");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    screenDim = frame.getToolkit().getScreenSize();
    screenWidth = (int) screenDim.getWidth();
    screenHeight = (int) screenDim.getHeight();

    contentPane = (JPanel) frame.getContentPane();
    label = new JLabel("Hello World!");
    contentPane.add(label);

    frame.pack();
    frameWidth = frame.getWidth();
    frameHeight = frame.getHeight();
    frame.setLocation(screenWidth / 2 - frameWidth / 2, screenHeight / 2
        - frameHeight / 2);
    frame.setVisible(true);
  }
}

	  

FrameUtilities

import javax.swing.JFrame;
import java.awt.Dimension;

class FrameUtilities {
  public static void centerAndShowFrame(JFrame frame, double xScale,
      double yScale) {
    Dimension screen;
    int screenWidth, screenHeight;
    int frameWidth, frameHeight;

    screen = frame.getToolkit().getScreenSize();
    screenWidth = (int) screen.getWidth();
    screenHeight = (int) screen.getHeight();

    frame.pack();
    frameWidth = frame.getWidth();
    frameHeight = frame.getHeight();
    frame.setSize((int) (frameWidth * xScale), (int) (frameHeight * yScale));
    frameWidth = frame.getWidth();
    frameHeight = frame.getHeight();
    frame.setLocation(screenWidth / 2 - frameWidth / 2, screenHeight / 2
        - frameHeight / 2);
    frame.setVisible(true);
  }
}

	  

Exercise Section2.4.  Tic Tac Toe with time counter (II)

To improve our knowledge about the Java Event Management model, we are going to propose the following exercise to do at home. We will be based on the TicTacToe GUI's code that we have programmed at the laboratory's session and we are going to add a new functionality that will permit to control the remaining turn's time for each player of the game. That's why we are going to incorporate a new component that is provided by the Java's graphics library (javax.swing) that acts as a "timer".

The component we are talking about is implemmented at the javax.swing.Timer class and, basically, it generates ActionEvent events periodically at constant intervals that can be configured previously. The way these events are managed by the component is seamlessly integrated on the Java's general "Event Delegated Management Model" that, let's remember, is based on the presence of three kinds of objects:

  • A set of Event Sources that generate events of a certain type as a consequence from user actions when interacting with them.

  • A set of Events that can be organised in a class hiereachy according to the stored information and the distinct origin of them. For example, ActionEvent type for user actions over a component; MouseEvent type for mouse movements; KeyEvent type for key strokes, etc..

  • A set of Listeners that react to of events that are generated by the precciding event sources according to the distinct types of events they are subscribed to. For that, they must implement the associated interface for the type of the event they want to manage: ActionListenter for ActionEvent events; MouseListenter for MouseEvent events; etc....

This model has many advantages: is understandable, flexible, extensible and robust. A good proof of this is how easy will result to add aditional Event Management logic code to our TicTacToe's GUI. To achieve that, let's make the corresponding Event Management analogy associated to the component that we are going to incorporate to the application:

  • The Source of Events will be the Timer component (javax.swing.Timer)

  • The Event type that is going to be generated and treated next is ActionEvent.

  • The Listener class (TimeListener), has to implement the associated interface of the Event's type: ActionListener.

The following image shows the initial window where the label with the player turn's time remaining information is located at the upper information's panel (10 seconds for this game).

The time counter starts discounting seconds until the player turn's time is over, in that case, a Dialog Window is shown with the corresponding error message to the user.

After the user has clicked the Accept button, the player's turn is changed and the time's counter is reset to the initial value, after that, it will start discounting seconds again.

Starting from the previous laboratory TicTacToe exericse's code you have implemented, make the following tasks to be able to incorporate the time counter to it:

  • Add the following attributes to the TicTacToe class that will be necessary to integrate the time counter:

    • A label (JLabel) that contains the text wiht the information of the remaining turn's time.

    • An integer constant (final static int) that represents the maximum turn's time.

    • A Timer reference (javax.swing.Timer) that will be the time counter.

  • Implement the TimeListener class that acts as a Listener class for the time counter's events.

    • Add a class attribute that should be a reference to the main window's game (JFrame) and it will be initialized at the class constructor.

    • Add another integer static attribute that will store the remaining number of seconds of the player's turn.

    • Implement the public void actionPerformed(ActionEvent event) method that will be executed periodically by the Timer's object. Such method will decrement the seconds counter and, in the case this value reaches to cero, it will show the corresponding error message to the uses. Finally, it will change the player's turn.

  • Create a Timer object's instance at the TicTacToe's constructor so that, it is started discounting the remainig turn's time from that moment.

    • As a constructor's argument, pass a TimeListener object's reference to it that has to be initialized with a window game's reference, that is, with this

  • Finally, update the method that serves to change the player's turn, public void changeTurn(), so that it should set the TimeListener second's static counter to cero. The counter will, therefore, start discounting the new player turn's remaining time.

Solutions

The exercise solution's code is shown next:

<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++;
    }

  }

}