4

I'm trying to write a Tic Tac Toe program using swing, but I seem to having some trouble. In my anonymous inner classes I attempt to set up the actionListener for each of my buttons, but I'm having trouble finding the type or the variable which will allow me to reference the buttons and set them to either X or Y. I tried e.getSource().setText() in my anonymous classes, but that came back with errors. Any thoughts? Thanks! Alex

import javax.swing.*;

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

public class TicTacToe  {

public JFrame frame;
public JLabel label;
public JPanel panel;

public static int counter;



public void go()
{ 
    frame = new JFrame("TicTacToe");
    frame.setSize(500, 500);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    panel = new JPanel();
    panel.setLayout(new GridLayout(3,3,10,10));
    frame.add(BorderLayout.CENTER, panel);
    label= new JLabel("TIC TAC TOE");
    frame.add(BorderLayout.NORTH, label);

    ; 


    JButton button1 = new JButton("Button 1");
    JButton button2 = new JButton("Button 1");
    JButton button3 = new JButton("Button 1");
    JButton button4 = new JButton("Button 1");
    JButton button5 = new JButton("Button 1");
    JButton button6 = new JButton("Button 1");
    JButton button7 = new JButton("Button 1");
    JButton button8 = new JButton("Button 1");
    JButton button9 = new JButton("Button 1");





    button1.addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent e)
        {


        }
    });

    button2.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {

        }
    });

    button3.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {

        }
    });

    button4.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {

        }
    });

    button5.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {

        }
    });

    button6.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {

        }
    });

    button7.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {

        }
    });

    button8.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {

        }
    });

    button9.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {

        }
    });

    panel.add(button1);
    panel.add(button2);
    panel.add(button3);
    panel.add(button4);
    panel.add(button5);
    panel.add(button6);
    panel.add(button7);
    panel.add(button8);
    panel.add(button9);
    frame.setVisible(true);
    panel.setVisible(true);

}


public static void main(String[] args)
{
    TicTacToe gui = new TicTacToe();
    gui.go();

}


}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
Amloelxer
  • 752
  • 2
  • 8
  • 20

3 Answers3

12

Remember, ActionListener can be used on a number of different types of components, so the source reference is generalized. You need to cast to back to the expected value

button9.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e)
    {
        Object source = e.getSource();
        if (source instanceof JButton) {
            JButton btn = (JButton)source;
            // Go ahead and do what you like
        }
    }
});

While I know your ActionListener as it stands can pretty much guarantee that the source type of the Object will be a JButton, I never like blindly casting objects, but that's me

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Another question. What would be the best way to determine a winner this way? Let's say if I make a counter and have ((JButton) source).setText("O"); or ((JButton) source).setText("X"); – Amloelxer Aug 05 '13 at 04:19
  • Actually, the best way would be to establish some kind of model. This would allow you to inspect the state of play and determine which cells are selected and by who. Each time a player selects a button, I would update the model. Part of this process would be for the model to check for a winner and fire an appropriate event – MadProgrammer Aug 05 '13 at 04:24
2

If you are receiving errors then you should post them but I am assuming it is because you aren't asserting that the source object is in fact a button. There are two straightforward solutions to what you are doing.

First is that since you are only adding one action listener to each button, you can assume that it is the object that the action event is referring to. Just note that the button either needs to be an instance variable or declared final:

    button1.addActionListener(new ActionListener(){ 
        @Override
        public void actionPerformed(ActionEvent e)
        {
            button1.setText("X/Y");
        }
    });

Second, which would fix the cause of your error, is by asserting that the ActionEvent source object is in fact a button. This is done by checking that the source is an instance of JButton:

    button1.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() instanceof JButton) {
                ((JButton) e.getSource()).setText("X/Y");
            }
        }
    });
Kevin Sheehan
  • 408
  • 3
  • 7
  • 1
    The only comment I would make is to make sure you highlight the fact that in your first example the `JButton` will either need to be a instance variable or `final` ;) – MadProgrammer Aug 05 '13 at 02:34
1

I went through and cleaned up your code a little bit. I'm not quite sure why your code had errors, but I didn't get any. I suggest, since you have common code, to reuse it like I did in an array. I also added a boolean to switch between players on each button click.

Lastly, I suggest setting up the JFrame in the constructor, or in a private method called by the constructor (more complex user interfaces might have a lot of code, and breaking it up is a good habit for maintainability of your code) like I showed below.

import javax.swing.*;

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

public class TicTacToe {
    public static final boolean PLAYER_X = false;
    public static final boolean PLAYER_O = true;

    public static int counter;

    private JFrame frame;
    private JLabel label;
    private JPanel panel;
    private JButton[] buttons;
    private boolean player;

    public TicTacToe() {
        frame = new JFrame("TicTacToe");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        panel = new JPanel();
        panel.setPreferredSize(new Dimension(500, 500));
        panel.setLayout(new GridLayout(3, 3, 10, 10));
        frame.add(BorderLayout.CENTER, panel);

        label = new JLabel("TIC TAC TOE");
        frame.add(BorderLayout.NORTH, label);

        /* Set the initial player turn to player X */
        player = PLAYER_X;

        /* Create the JButtons  */
        buttons = new JButton[9];

        /* Loop through and set all of them up */
        for (int i = 0; i < buttons.length; i++) {
            buttons[i] = new JButton();
            buttons[i].addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (e.getSource() instanceof JButton) {
                        ((JButton)e.getSource()).setText(player ? "O" : "X"); /* Set button text */
                        player = !player; /* Switch turns */
                    }
                }
            });

            /* Add all of the buttons to the panel. */
            panel.add(buttons[i]);
        }

        /* Pack the frame to the contents. Basically a "fit to contents". */
        frame.pack();
    }

    public void go() {
        frame.setVisible(true);
        panel.setVisible(true);
    }

    public static void main(String[] args) {
        TicTacToe gui = new TicTacToe();
        gui.go();
    }
}
Pandacoder
  • 708
  • 7
  • 11