0

I've been making a tic tac toe game for school that takes user input through the console and then displays the current board state in GUI. I have all of the code to make sure the game runs as it is supposed to, however I can't figure out how to draw the X or O images onto the board as the players play. It draws the board fine when the constructor is called, but it doesn't change at all when I call my methods to draw the images. I don't know a whole lot about GUI and absolutely any help is appreciated.

My code:

package part1;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame; 
import javax.swing.JPanel; 
public class boardDraw extends JFrame{
private ImageIcon XIcon = new ImageIcon("XJava.png"); 
private ImageIcon OIcon = new ImageIcon("OJava.png"); 
private Image X = XIcon.getImage(); 
private Image O = OIcon.getImage(); 
private Point[][] coords = new Point[3][3]; 
public boardDraw(){
    setTitle("Tic-Tac-Toe");
    setSize(1000, 1000); 
    setVisible(true); 
    setDefaultCloseOperation(EXIT_ON_CLOSE); 
    fillCoords(); 
}

public void paint(Graphics g){
    g.drawLine(0, 333, 1000, 333);
    g.drawLine(0, 666, 1000, 666);
    g.drawLine(333, 0, 333, 1000);
    g.drawLine(666, 0, 666, 1000);

}


public void fillCoords(){
    int x = 170; 
    int y = 170; 
    for(int i = 0; i<3; i++){
        for(int j = 0; j<3; j++){
            coords[i][j] = new Point(x, y); 
            y += 333; 
        }
        x += 333; 
    }
}

public void setX(int row, int col){
    Graphics g = this.getGraphics();
    //g.drawImage(X, (int)coords[row][col].getX(), (int)coords[row][col].getY(), null);
    //System.out.println(X); 
}

public void setO(int row, int col){
    Graphics g = this.getGraphics();
    g.drawImage(O, (int)coords[row][col].getX(), (int)coords[row][col].getY(), this);
    //System.out.println(O); 
    //g.drawOval((int)coords[row][col].getX(), (int)coords[row][col].getY(), 100, 100);

}

}

  • 1
    *"that takes user input through the console"* would be your first problem, console input is linear, GUIs are event driven, they are different paradigms which aren't easily mixed – MadProgrammer Feb 24 '17 at 05:51
  • 1
    Don't use getGraphics(). Don't override paint() on a JFrame. Read the section from the Swing tutorial on [Custom Painting](http://docs.oracle.com/javase/tutorial/uiswing/painting/index.html) for painting basics and some working examples to get your started. Also, class names should start with an upper case character. Follow Java conventions. – camickr Feb 24 '17 at 05:52
  • Your next problem is with your `paint` method, this is not how custom painting should be done, generally speaking you should not override `paint` of top level components, they are to complex and generally have child components already added to them, instead, start with a `JPanel` and override it's `paintComponent` method instead – MadProgrammer Feb 24 '17 at 05:53
  • [As an example](http://stackoverflow.com/questions/14116093/how-to-set-the-color-of-jbuttons-in-an-array-in-java/14116356#14116356) – MadProgrammer Feb 24 '17 at 06:11
  • 1) See [Detection/fix for the hanging close bracket of a code block](http://meta.stackexchange.com/q/251795/155831) for a problem I could no longer be bothered fixing. 2) One way to get image(s) for an example is to hot link to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). 3) I would not advise using custom painting for this. Instead, put 9 labels (`JLabel`) in a grid layout (`new GridLayout(3,3,n,n); // n is padding between cells`) and use the images as icons for the labels. – Andrew Thompson Feb 24 '17 at 06:19

1 Answers1

3

..how to draw a picture onto an already existing JFrame(?)

I would not advise using custom painting for this. Instead, put 9 labels (JLabel) in a grid layout (new GridLayout(3,3,n,n); // n is padding between cells) and use the images as icons for the labels.

Imagine these are the images.

  • X

  • O

Then this might be the end result:

enter image description here

This is the code that puts it together:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class TicTacToe {

    private JComponent ui = null;
    private BufferedImage xImage;
    private BufferedImage oImage;
    private BufferedImage blankImage;

    TicTacToe() {
        initUI();
    }

    public void initUI() {
        if (ui != null) {
            return;
        }

        ui = new JPanel(new BorderLayout(4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));

        try {
            xImage = ImageIO.read(new URL("https://i.stack.imgur.com/F0JHK.png"));
            oImage = ImageIO.read(new URL("https://i.stack.imgur.com/gJmeJ.png"));
            blankImage = new BufferedImage(
                    xImage.getWidth(), xImage.getHeight(),
                    BufferedImage.TYPE_INT_ARGB);
        } catch (IOException ex) {
            System.err.println("Unable to load icons!");
            ex.printStackTrace();
            System.exit(1);
        }
        JPanel ticTacToeBoard = new JPanel(new GridLayout(3, 3, 4, 4));
        ui.add(ticTacToeBoard, BorderLayout.CENTER);

        Random r = new Random();
        Image[] images = {
            xImage,
            oImage,
            blankImage
        };
        for (int ii = 0; ii < 9; ii++) {
            // next line is to show how it might look.
            Image image = images[r.nextInt(3)];
            JLabel l = new JLabel(new ImageIcon(image));
            l.setOpaque(true);
            l.setBackground(Color.WHITE);
            ticTacToeBoard.add(l);
        }
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception useDefault) {
                }
                TicTacToe o = new TicTacToe();

                JFrame f = new JFrame(o.getClass().getSimpleName());
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                f.setLocationByPlatform(true);

                f.setContentPane(o.getUI());
                f.pack();
                f.setMinimumSize(f.getSize());

                f.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

Edit

I forgot that the ultimate goal is for the user to be able to interact with the squares using keyboard and mouse! :P

Taking that into account, note that we can only add a mouse listener to a label. For keyboard interaction as well, a button (JButton) would be better. Once an ActionListener is added to a button, it will react to either keyboard or mouse input. See Making a robust, resizable Swing Chess GUI for a board game based on an array of buttons. E.G.

JButton[][] chessBoardSquares = new JButton[8][8];
Community
  • 1
  • 1
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433