Jordyn
Jordyn

Reputation: 55

Unable to draw on JPanels

I'm trying understand the MouseActionListener/Adapter class as well as Graphics and I thought that I'd do so by creating a Tic-Tac-Toe game. I didn't wan't to use JButtons because I'd just be changing the wording on the button rather than drawing a graphic image. I'm having trouble getting my panels to draw an image. I created two inner classes one for the JPanels called Board and one for the MouseAdapter class. My paint method is located in the Board class but when I click the JPanel it does not draw the graphic (so far I just set to draw an X). I know that it recognizes that a panel was clicked through using the debugger but I don't understand why it does not invoke the paint method when called.

Here's my code:

package xno;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics.*;
/**
 *
 * @author jordynsmith
 */
public class XnOFrame extends JFrame {
    private int win=0;
    private int loss=0;
    private int turn=1; //keeps track of the turn so that color can change with each turn
    public XnOFrame(){
        super("X n' O");

    //instantiate
    board = new Board[3][3];
    for(int i=0 ;i< 3; i++){
        for(int j=0; j<3; j++){
            board[i][j] = new Board();
            board[i][j].setStatus("Empty");
            board[i][j].setBackground(Color.black);
            //creates tic tac toe border
            //1st column
            if(j==0){//top
                    board[i][j].setBorder(BorderFactory.createMatteBorder(0, 0, 2, 2, Color.blue));
                if(i==1){//center
                    board[i][j].setBorder(BorderFactory.createMatteBorder(2, 0, 2, 0, Color.blue));    
                }
                if(i==2){//bottom
                    board[i][j].setBorder(BorderFactory.createMatteBorder(2, 0, 0, 0, Color.blue));
                }
            }
            //2nd column
            else if(j==1){//top
            board[i][j].setBorder(BorderFactory.createMatteBorder(0, 2, 2, 2, Color.blue));
                if(i==1){//center
                board[i][j].setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, Color.blue));    
                }
                if(i==2){//bottom
                board[i][j].setBorder(BorderFactory.createMatteBorder(2, 2, 0, 2, Color.blue));
                }
            }
            //3rd column
            else if(j==2){//top
            board[i][j].setBorder(BorderFactory.createMatteBorder(0, 2, 2, 0, Color.blue));
                if(i==1){//center
                board[i][j].setBorder(BorderFactory.createMatteBorder(2, 2, 2, 0, Color.blue));    
                }
                if(i==2){//bottom
                board[i][j].setBorder(BorderFactory.createMatteBorder(2, 2, 0, 0, Color.blue));
                }
            }
        }
    }
    goBtn = new JButton("Start Game");
    quitBtn = new JButton("End Game");
    newBtn = new JButton("New Game");
    score = new JLabel("Score");
    wins = new JLabel("Wins");
    losses = new JLabel("Losses");
    wPoints = new JLabel(win + "");
    lPoints = new JLabel(loss + "");
    line = new JLabel("||");
    menuPanel = new JPanel();
    scorePanel = new JPanel();
    gridPanel = new JPanel();
    scoreLabelPanel = new JPanel();
    pointsPanel = new JPanel();

    //set layout
    this.setLayout(new BorderLayout());
    menuPanel.setLayout(new FlowLayout());
    scorePanel.setLayout(new BorderLayout());
    gridPanel.setLayout(new GridLayout(3,3));
    scoreLabelPanel.setLayout(new FlowLayout());
    pointsPanel.setLayout(new FlowLayout());

    //add components
    this.add(gridPanel, BorderLayout.CENTER);
    this.add(menuPanel, BorderLayout.SOUTH);
    this.add(scorePanel, BorderLayout.WEST);
    menuPanel.add(goBtn);
    menuPanel.add(quitBtn);
    menuPanel.add(newBtn);
    scorePanel.add(scoreLabelPanel, BorderLayout.NORTH);
    scorePanel.add(pointsPanel, BorderLayout.CENTER);
    scoreLabelPanel.add(score);
    pointsPanel.add(wins);
    pointsPanel.add(wPoints);
    pointsPanel.add(line);
    pointsPanel.add(losses);
    pointsPanel.add(lPoints);
    for(int i=0; i<board.length; i++){
        for(int j=0; j<board[i].length; j++){
            gridPanel.add(board[i][j]);
        }
    }

    //adding action
    for(int i=0; i<board.length; i++){
        for(int j=0; j<board[i].length; j++){
            board[i][j].addMouseListener(new BoardAction());
        }
    }
}

This is where my problem really is. I'm not really sure how to organize and when to call certain methods, and what should be called to get this to work.

private class BoardAction extends MouseAdapter{
    private Board panel = new Board();

    @Override
    public void mouseClicked(MouseEvent me){
        //determines which panel was clicked
        panel = (Board) me.getSource();
        panel.repaint();//calls paint method
    }
}



private class Board extends JPanel{
    public String status;

    public Board(){
        status="Empty";
    }

    public String getStatus(){
        return status;
    }
    public void setStatus(String s){
        status = s;
    }

    public void piant(Graphics g){
    super.paint(g);
    draw(g);
    }

    private void draw(Graphics g){
        if(status.equals("Empty")){//paints x if empty
           if(turn%2 != 0){//if turn is odd the color is green
            g.setColor(Color.green);
           }
           else//if even the color is yellow
               g.setColor(Color.yellow);

           g.drawLine(0, 0, getWidth(), getHeight());
           g.drawLine(getHeight(), 0, getWidth(), 0);
           setStatus("X");
       } 
    }
}

//components
Board [][] board;
JPanel menuPanel, scorePanel, gridPanel, scoreLabelPanel, pointsPanel;
JButton goBtn, quitBtn, newBtn;
JLabel score, wins, losses, wPoints, lPoints, line;

}

So after changing my paint method to paintComponent a StackOverflowError is thrown when the super.paint() method is called and my JButtons take forever to appear. Just to see what happens I commented that line out and ran the program. In doing that I get this: The black panels are the one's that I have clicked. The panels should be black to start and when clicked the green line should show up. Additionally, only one of the green lines show up (there should be two since it's an x). I'm doing my best to follow the tutorials but any additional help is greatly appreciated!

Upvotes: 0

Views: 60

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347184

Don't change the status of the component inside paint

private  void draw(Graphics g){
    if(status.equals("Empty")){//paints x if empty
       if(turn%2 != 0){//if turn is odd the color is green
        g.setColor(Color.green);
       }
       else//if even the color is yellow
           g.setColor(Color.yellow);

       g.drawLine(0, 0, getWidth(), getHeight());
       g.drawLine(getHeight(), 0, getWidth(), 0);
       // This is bad idea...
       setStatus("X");
   } 
}

A paint method should do nothing more the paint the current state of the component, it should never modify it.

Prefer using paintComponent over paint. See Painting in AWT and Swing and Performing Custom Painting for more details

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    draw(g);
}

private void draw(Graphics g) {
    if (status.equals("Empty")) {//paints x if empty
        if (turn % 2 != 0) {//if turn is odd the color is green
            g.setColor(Color.green);
        } else//if even the color is yellow
        {
            g.setColor(Color.yellow);
        }

        g.drawLine(0, 0, getWidth(), getHeight());
        g.drawLine(getHeight(), 0, getWidth(), 0);
    }
}

Your BoardAction should actually change the status of the Board in some meaningful way...

private class BoardAction extends MouseAdapter {

    @Override
    public void mouseClicked(MouseEvent me) {
        //determines which panel was clicked
        Board panel = (Board) me.getSource();
        panel.setStatus("X");
        panel.repaint();//calls paint method
    }
}

Consider also, you Board class should also override getPreferredSize and return some size other then 0x0

Upvotes: 3

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

public void piant(Graphics g){

piant != paint.

  1. You should override paintComponent
  2. Always preface every override with the @Override annotation so the compiler will tell you when you're doing it wrong.
  3. Read the Swing painting tutorials.

Upvotes: 3

Related Questions