简体   繁体   中英

How do I pass an object from another class into the paintComponent method?

My assignment is to create the game "FloodIt." You can play the game here if you need to understand it, but I don't think it's really necessary: http://unixpapa.com/floodit/

I have finished the actual game part of it, but now I need to make a graphical interface for it. I have three classes:

Board.java, which makes the board with random int values and contains several other methods to make the game work:

import java.awt.Color;
import java.util.Random;


/**The board class for the Flood-It game.  This class implements a NxN board filled with numColors colors.
 * The class implements several methods to allow the playing of the game.
 */

class Board {

//you will probably need to create some field variables
private int size;
private int numColors;
private int[][] board;
private int numOfMoves;
/**Constructs a new sizeXsize board filled where each element on the board is a random number between 0
 * and numColors.  Also initializes the number of moves to zero.
 * @param size -- the size of the board
 * @param numColors -- the number of possible entries on the board
 */
public Board(int size,int numColors) {
    //TODO finish this constructor
    this.size = size;
    this.numColors = numColors;
    numOfMoves = 0;



    board = new int[size][size];
    Random rand = new Random();
    int randomNum = 0;
    for (int count = 0; count < size; count++) {
        for (int counter = 0; counter < size; counter++) {
            randomNum = rand.nextInt(this.numColors);
            board[count][counter] = randomNum;
        }
    }


}


/**Updates the board to fill (from the top left corner) with a specified color.  
 * Filling stops when any other color is hit besides the one in the top left corner.
 * Play the game at http://www.lemoda.net/javascript/flood-it/ or http://unixpapa.com/floodit/?sz=14&nc=4
 * to get a better understanding of what this method should do.
 * You will probably also want to take a look at the algorithm described
 * at http://en.wikipedia.org/wiki/Flood_fill which describes what this method should do.
 * I recommend the Stack-based recursive implementation.  It is a recursive algorithm for
 * flooding the board.  It is one of the easier ones to implement.
 * You are free to have this method call other methods.  I would recommend creating a private method that
 * this method calls and have that private method be the recursive method.
 * A recursive method is one that calls itself.
 * @param color -- the new color to flood the board with.
 */
public void move(int replacementColor) {
    int targetColor = board[0][0];
    recursiveMove(0,0,targetColor,replacementColor);
    numOfMoves++;
}

private void recursiveMove(int xCoord, int yCoord, int targetColor, int replacementColor) {
    if (targetColor == replacementColor) {
        return;
    }
    if (board[xCoord][yCoord] != targetColor) {
        return;
    }

    board[xCoord][yCoord] = replacementColor;
    if (yCoord != size-1) {
        recursiveMove(xCoord,yCoord+1,targetColor,replacementColor);
    }
    if (yCoord != 0) {
        recursiveMove(xCoord,yCoord-1,targetColor,replacementColor);
    }
    if (xCoord != 0) {
        recursiveMove(xCoord-1,yCoord,targetColor,replacementColor);
    }
    if (xCoord != size-1) {
        recursiveMove(xCoord+1,yCoord,targetColor,replacementColor);
    }
}


/**returns true if the board is not completely filled with a single color.
 * Otherwise it returns false.
 * @return true if board is all one color
 */
public boolean finished() {
    //TODO finish this method
    for (int count = 0; count < size; count++) {
        for (int counter = 0; counter < size; counter++) {
            if (board[count][counter] != board[0][0]) {
                return false;
            }
        }
    }
    return true;
}


/**returns how many times the move() method has been called.
 * @return the number of times the move() method has been called.
 */
public int numMoves() {
    //TODO finish this method
    return numOfMoves;
}


/**Returns a string representation of the board.  Use tabs between elements of the board.
 * And have every row of the board be separated by a newline character.
 * Example:
 * "1\t0\t3\t\n2\t0\t2\t\n1\t0\t1\t\n"
 * @return a String representation of the board
 */
public String toString() {
    //TODO finish this method
    String boardString = "";
    for (int count = 0; count < board.length; count++) {
        for (int counter = 0; counter < board.length; counter++) {
            boardString += board[count][counter];
            boardString += "\t";
        }
        boardString += "\n";
    }
    return boardString;
}
}

FloodIt.java, which contains the JFrame lines in order to load the graphical interface, as well as code to actually run the game (it's not entirely finished, as I got stuck):

import java.util.Scanner;
import javax.swing.JFrame;

/**This class is the main method for the Flood-It game as found on many web sites 
 * ( such as http://www.lemoda.net/javascript/flood-it/ or 
http://unixpapa.com/floodit/?sz=14&nc=4 ).
 * It prompts the user for the size of the board
 * and the number of colors.  The user is prompted for the next color until the board is flooded.
 * After the game is over it prints how many turns the user took and then asks if they want to play again.
 */
class FloodIt {

private static final int FRAMESIZE = 1000;

public static void main(String args[]) {
    JFrame frame = new JFrame();
    frame.setSize(FRAMESIZE,FRAMESIZE);
    frame.setTitle("Brennan's Game");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    GraphicalBoard component = new GraphicalBoard();
    frame.add(component);
    frame.setVisible(true);

    String again="";
    int size = 20;
    int numColors = 7;
    do {
        Board board=new Board(size,numColors);
        while(!board.finished()) {
            //I will change the print statements below into graphical input boxes later
            System.out.print("****************\n"+board+"\n****************\n");
            System.out.print("What color do you choose? ");
            int color=Integer.parseInt(scan.nextLine());
            board.move(color);
        }
        System.out.println("Nice job, you finished in "+board.numMoves());
        System.out.print("Would you like to play again (Y/N)? ");
        again=scan.nextLine();
    } while (again.equalsIgnoreCase("Y"));
    scan.close();
}
}

And GraphicalBoard.java, which is supposed to take the values of the 2d array from Board.java and display the board in a graphical interface. Each number that could be in the 2d array corresponds with a color in the Colors array:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;

public class GraphicalBoard extends JComponent {
private int xSize = 50;
private int ySize = 50;
public void paintComponent(Graphics g, int size, Board board) {
    String colors[] = {"BLUE","GREEN","YELLOW","RED","BLACK","ORANGE","PINK"};
    Graphics2D g2 = (Graphics2D) g;
    int xCoord = 0;
    int yCoord = 0;
    int colorNum = 0;
    String colorOfSquare = "";
    for (int count = 0; count < size; count++) {
        for (int counter = 0; counter < size; counter++) {
            colorNum = board[count][counter];
            colorOfSquare = colors[colorNum];
            g2.setColor(Color.colorOfSquare);
            Rectangle square = new Rectangle(xCoord,yCoord,xSize,ySize);
            xCoord += 50;
        }
        yCoord += 50;
    }
}

}

Two problems:

  1. In GraphicalBoard.java, on the line "colorNum = board[count][counter];", I am getting the error: "The type of expression must be an array type but it resolved to Board."

I seem to be having a problem bring over the already initialized board from the Board.java class into the GraphicalBoard.java class.

  1. In GraphicalBoard.java, on the line "g2.setColor(Color.colorOfSquare);", I am getting the error: "colorOfSquare cannot be resolved or it not a field."

I know the problem, it is supposed to be something like "g2.setColor(Color.BLACK);", but I am going to have the user input the color, so it kind of needs to be a variable and I was hoping to have something cleaner than just an if statement for every color.

Any suggestions? Thanks!

  1. Your Board class contains a member variable int[][] board , but its scope is private. When you call the following:

    colorNum = board[count][counter];

This is wrong because the board variable here is an object of Board class. It itself is not the two day array, but it encapsulates int[][] board inside it. So you need to provide a getter method in Board to expose its board member variable like this:

public int[][] getBoard() {
  return board;
} 

Then in the paintComponent method you can access it as: board.getBoard()[count][counter] .

  1. You already seem to have a user inputted color in the colorOfSquare variable. But Graphics2D 's setColor method would only accept a variable of type java.awt.Color. Since you have the String representation of the color, you can get its corresponding java.awt.Color value using reflection as mentioned here . The following should work for you:

     Color color; try { Field field = Color.class.getField(colorOfSquare); color = (Color) field.get(null); } catch (Exception e) { color = null; // Not defined } 

Two answers:

  1. paintComponent ONLY receives a Graphics object. See this link for a short tutorial. If you need to access other objects in this method, make them variables of GraphicalBoard and pass them om during construction.

1.5 You need to access the Board's board, as this is what you are using. So add a getBoard(int i, int j) in class Board . Something like the following (I also added a getSize() method) :

public int getBoard(int i, int j) {
  return board[i][j] ;
} 

public int getSize() {
   return size;
} 
  1. Your color colorOfSquare is already defined as a color. The error arises because the Color class doesn't have such a constant. You should just pass the color directly.

Try this:

  public class GraphicalBoard extends JComponent { 

     private int xSize = 50; 
     private int ySize = 50; 
     private Board board;
     private int size;

      public GraphicalBoard() {
       } 

      public void setBoard(Board board){
         this.board = board;
     } 

      public void paintComponent(Graphics g) { 
        super.paintComponent(); 

        if(board == null) {
          throw new RuntimeException("Board not set") ;
       } 

         String colors[] = {"BLUE","GREEN","YELLOW","RED","BLACK","ORANGE","PINK"}; 
         Graphics2D g2 = (Graphics2D) g; 
         int xCoord = 0; 
         int yCoord = 0; 
         int colorNum = 0; 
         int size = board.getSize() ;
        String colorOfSquare = ""; 

        for (int count = 0; count < size; count++) { 

            for (int counter = 0; counter < size; counter++) { 
               colorNum = board.getBoard(count, counter) ; 
               colorOfSquare = colors[colorNum]; 
               g2.setColor(colorOfSquare); 
               Rectangle square = new Rectangle(xCoord,yCoord,xSize,ySize); 
               xCoord += 50; 
             } 
           yCoord += 50;
         } 
}

In very general terms,

  1. your view, here the drawing JPanel, should contain a reference to the model object via a has-a or "composition" structure
  2. The view should be notified of changes in the model, often via event listeners such as a PropertyChangeListener
  3. The view then extracts the key information and uses that to help decide what to draw. So, if it has a reference to the current Board object, it can make getter method calls from within the paintComponent method.

Other issues in your code:

  • Make sure to call the super's paintComponent within your override, else you will not clean up "dirty" pixels
  • Don't mix linear code -- a Scanner based on System.in with GUI code. Make it all one or the other.

For example, in the code below, I use a model class, the class that holds the int[][] board variable and here called BoardModel, and I give it a SwingPropertyChangeSupport object.

class BoardModel {
    // .....
    private SwingPropertyChangeSupport support = new SwingPropertyChangeSupport(this);

This object will accept listeners, and will allow me to notify listeners of changes to the model.

public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
    support.addPropertyChangeListener(propertyName, listener);
}

Then when the model changes, I notify all listeners by calling the support object's firePropertyChange(...) method:

public void selectSquare(int x, int y) {
    int replacementValue = board[y][x];
    int targetValue = board[0][0];
    if (targetValue == replacementValue) {
        return;
    } else {
        recursiveMove(0, 0, targetValue, replacementValue);
        numOfMoves++;
        support.firePropertyChange(BOARD, null, board);  // ***** here
        setWin(checkForWin());
    }
}    

Then in the control, I can add listeners that notify the view of changes:

       model.addPropertyChangeListener(BoardModel.BOARD, new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent e) {
                view.repaint();
                String moveCount = "" + model.getNumOfMoves();
                controlPanel.setMoveCountFieldText(moveCount);
            }
        });
        model.addPropertyChangeListener(BoardModel.WIN, new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if ((boolean) evt.getNewValue()) {
                    String message = "Move count: " + model.getNumOfMoves();
                    String title = "Game Over";
                    int messageType = JOptionPane.PLAIN_MESSAGE;
                    JOptionPane.showMessageDialog(view, message, title, messageType);
                }
            }
        });

A working example could look like this:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.SwingPropertyChangeSupport;

public class BoardFun {
    private static final int NUM_COLORS = 6;
    private static final int SIZE = 20;

    @SuppressWarnings("serial")
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            int size = SIZE;
            int numColors = NUM_COLORS;
            final BoardModel model = new BoardModel(size , numColors );
            final BoardPanel view = new BoardPanel();
            final ControlPanel controlPanel = new ControlPanel();

            view.setModel(model);
            view.addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent mEvt) {
                    Point p = mEvt.getPoint();
                    int row = view.getRow(p);
                    int col = view.getColumn(p);
                    model.selectSquare(col, row);
                }
            });
            model.addPropertyChangeListener(BoardModel.BOARD, new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent e) {
                    view.repaint();
                    String moveCount = "" + model.getNumOfMoves();
                    controlPanel.setMoveCountFieldText(moveCount);
                }
            });
            model.addPropertyChangeListener(BoardModel.WIN, new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if ((boolean) evt.getNewValue()) {
                        String message = "Move count: " + model.getNumOfMoves();
                        String title = "Game Over";
                        int messageType = JOptionPane.PLAIN_MESSAGE;
                        JOptionPane.showMessageDialog(view, message, title, messageType);
                    }
                }
            });
            controlPanel.setResetAction(new AbstractAction("Reset") {

                @Override
                public void actionPerformed(ActionEvent e) {
                    model.reset();
                }
            });

            JFrame frame = new JFrame("Game");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(view);
            frame.add(controlPanel, BorderLayout.PAGE_START);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

@SuppressWarnings("serial")
class ControlPanel extends JPanel {
    private JTextField moveCountField = new JTextField("0", 10);
    private JButton resetButton = new JButton();

    public ControlPanel() {
        add(new JLabel("Move Count:"));
        add(moveCountField);
        add(resetButton);
    }

    public void setResetAction(Action action) {
        resetButton.setAction(action);
    }

    public void setMoveCountFieldText(String text) {
        moveCountField.setText(text);
    }
}

@SuppressWarnings("serial")
class BoardPanel extends JPanel {
    private static final int PREF_W = 640;
    private static final int PREF_H = PREF_W;
    private BoardModel model;
    private Color[] colors;

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        } else {
            return new Dimension(PREF_W, PREF_H);
        }
    }

    public void setModel(BoardModel model) {
        this.model = model;
        colors = new Color[model.getNumColors()];

        // create colors.length Colors, all of different hue
        for (int i = 0; i < colors.length; i++) {
            float hue = (float) i / colors.length;
            colors[i] = Color.getHSBColor(hue, 1f, 1f);
        }
    }

    // translate point to logical square position
    int getRow(Point p) {
        return (p.y * model.getBoard().length) / getHeight();
    }

    int getColumn(Point p) {
        return (p.x * model.getBoard()[0].length) / getWidth();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);  // always call the super's method
        if (model == null) {
            return;
        }
        int board[][] = model.getBoard();
        int height = getHeight() / board.length;
        int width = getWidth() / board[0].length;
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                Color color = colors[board[i][j]];
                g.setColor(color);
                int x = (j * getWidth()) / board[0].length;
                int y = (i * getHeight()) / board.length;
                g.fillRect(x, y, width, height);
            }
        }
    }
}

class BoardModel {
    public static final String BOARD = "board";
    public static final String WIN = "win";
    private int[][] board;
    private int numColors;
    private Random random = new Random();
    private SwingPropertyChangeSupport support = new SwingPropertyChangeSupport(this);
    private int numOfMoves = 0;
    private boolean win = false;

    public BoardModel(int size, int numColors) {
        board = new int[size][size];
        this.numColors = numColors;
        reset();
    }

    public void reset() {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                board[i][j] = random.nextInt(numColors);
            }
        }
        numOfMoves = 0;
        support.firePropertyChange(BOARD, null, board);
        setWin(false);
    }

    public int[][] getBoard() {
        return board;
    }

    public int getNumOfMoves() {
        return numOfMoves;
    }

    public int getNumColors() {
        return numColors;
    }

    public void setWin(boolean win) {
        boolean oldValue = this.win;
        boolean newValue = win;
        this.win = win;
        support.firePropertyChange(WIN, oldValue, newValue);
    }

    public boolean isWin() {
        return win;
    }

    public void selectSquare(int x, int y) {
        int replacementValue = board[y][x];
        int targetValue = board[0][0];
        if (targetValue == replacementValue) {
            return;
        } else {
            recursiveMove(0, 0, targetValue, replacementValue);
            numOfMoves++;
            support.firePropertyChange(BOARD, null, board);
            setWin(checkForWin());
        }
    }



    public boolean checkForWin() {
        int value = board[0][0];
        for (int[] row : board) {
            for (int cell : row) {
                if (cell != value) {
                    return false;
                }
            }
        }
        return true;
    }

    private void recursiveMove(int i, int j, int targetValue, int replacementValue) {
        int currentValue = board[i][j];
        if (currentValue != targetValue || currentValue == replacementValue) {
            return;
        }
        board[i][j] = replacementValue;
        int rowMin = Math.max(0, i - 1);
        int rowMax = Math.min(board.length - 1, i + 1);
        int colMin = Math.max(0, j - 1);
        int colMax = Math.min(board[i].length - 1, j + 1);
        for (int i2 = rowMin; i2 <= rowMax; i2++) {
            if (i2 != i) {
                recursiveMove(i2, j, targetValue, replacementValue);
            }
        }
        for (int j2 = colMin; j2 <= colMax; j2++) {
            if (j2 != j) {
                recursiveMove(i, j2, targetValue, replacementValue);
            }
        }
    }

    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        support.addPropertyChangeListener(propertyName, listener);
    }   
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM