Reputation: 55
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
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
Reputation: 285405
public void piant(Graphics g){
piant != paint.
paintComponent
@Override
annotation so the compiler will tell you when you're doing it wrong.Upvotes: 3