Consider a 2D grid with n rows
and n columns
(here 75x75). The symbols (tokens) are drawn in each cell on mouse click. The code below is used to draw grid lines and symbols within cells:
class DrawCanvas extends JPanel{
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
setBackground(Color.WHITE);
//Lines
g.setColor(Color.BLACK);
for(int ligne = 1; ligne < ROWS; ++ligne){
g.fillRoundRect(0, cellSize * ligne - halfGridWidth, canvasWidth - 1,
gridWidth, gridWidth, gridWidth);
}
for(int colonne = 1; colonne < COLS; ++colonne){
g.fillRoundRect(cellSize * colonne - halfGridWidth, 0
, gridWidth, canvasHeight - 1,
gridWidth, gridWidth);
}
//Symbols
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke(symbolStrokeWidth,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
for(int ligne = 0; ligne < ROWS; ++ligne){
for(int colonne = 0; colonne < COLS; ++colonne){
int x1 = colonne * cellSize + cellPadding;
int y1 = ligne * cellSize + cellPadding;
if(board[ligne][colonne] == Token.CERCLE_ROUGE){
g2d.setColor(Color.RED);
g2d.drawOval(x1, y1, symbolSize, symbolSize);
g2d.fillOval(x1, y1, symbolSize, symbolSize);
} else
if(board[ligne][colonne] == Token.CERCLE_BLEU){
int x2 = colonne * cellSize + cellPadding;
g2d.setColor(Color.BLUE);
g2d.drawOval(x1, y1, symbolSize, symbolSize);
g2d.fillOval(x2, y1, symbolSize, symbolSize);
}
}
}
With the code below, I can find all neighbors of a given cell:
private void neighbours(int col, int row) {
//find all serouding cell by adding +/- 1 to col and row
for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1 ) {
for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1 ) {
//if not the center cell
if(! ((colNum == col) && (rowNum == row))) {
//make sure it is within grid
if(withinGrid (colNum, rowNum)) {
System.out.println("Neighbor of "+ col+ " "+ row + " - " + colNum +" " + rowNum );
}
}
}
}
}
//define if cell represented by colNum, rowNum is inside grid
private boolean withinGrid(int colNum, int rowNum) {
if((colNum < 0) || (rowNum <0) ) {
return false; //false if row or col are negative
}
if((colNum >= COLS) || (rowNum >= ROWS)) {
return false; //false if row or col are > 75
}
return true;
}
Consider cells contents:
public enum Token{
VIDE, CERCLE_BLEU, CERCLE_ROUGE
}
Now I want to know if there is a way to determinate what a cell contains: is it empty: Token.VIDE
, has Token.CERCLE_BLEU
or has Token.CERCLE_ROUGE.
And how I can achieve that.
UPDATE: Below is my code:
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public final class Pha extends JFrame {
public static int ROWS = 75;
public static int COLS = 75;
public static int cellSize = 15;
public static int canvasWidth = cellSize * COLS + (ROWS *4) ;
public static int canvasHeight = cellSize * ROWS ;
public static int gridWidth = 1;
public static int halfGridWidth = gridWidth / 2;
public static int cellPadding = cellSize / 5;
public static int symbolSize = cellSize - cellPadding * 2;
public static int symbolStrokeWidth = 3;
public enum GameState{
JOUE, NUL, CERCLE_ROUGE_GAGNE, CERCLE_BLEU_GAGNE
}
private GameState actualState;
public enum Token{
VIDE, CERCLE_ROUGE, CERCLE_BLEU
}
private Token actualPlayer;
private Token[][] board;
private final DrawCanvas canvas;
private JLabel statusBar;
public Pha(){
canvas = new DrawCanvas();
canvas.setPreferredSize(new Dimension(canvasWidth, canvasHeight));
canvas.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int selectedRow = y / cellSize;
int selectedCol;
selectedCol = x / cellSize;
if(actualState == GameState.JOUE){
if(selectedRow >= 0 && selectedRow < ROWS && selectedCol >= 0
&& selectedCol < COLS &&
board[selectedRow][selectedCol] == Token.VIDE){
board[selectedRow][selectedCol] = actualPlayer;
updateGame(actualPlayer, selectedRow, selectedCol);
actualPlayer = (actualPlayer == Token.CERCLE_BLEU)? Token.CERCLE_ROUGE : Token.CERCLE_BLEU;
neighbours(selectedRow, selectedCol);
}
} else {
initGame();
}
repaint();
}
});
statusBar = new JLabel(" ");
statusBar.setFont(new Font(Font.DIALOG_INPUT, Font.ITALIC, 15));
statusBar.setBorder(BorderFactory.createEmptyBorder(2, 5, 4, 5));
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(canvas, BorderLayout.EAST);
cp.add(statusBar, BorderLayout.NORTH);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setTitle("Pha par esQmo");
setVisible(true);
board = new Token[ROWS][COLS];
initGame();
}
public void initGame(){
for(int ligne = 0; ligne < ROWS; ++ligne){
for(int colonne = 0; colonne < COLS; ++colonne){
board[ligne][colonne] = Token.VIDE;
}
}
actualState = GameState.JOUE;
actualPlayer = Token.CERCLE_ROUGE;
}
public void updateGame(Token theSeed, int ligneSelectionnee, int colonneSelectionnee) {
if (aGagne(theSeed, ligneSelectionnee, colonneSelectionnee)) {
actualState= (theSeed == Token.CERCLE_ROUGE) ? GameState.CERCLE_ROUGE_GAGNE : GameState.CERCLE_BLEU_GAGNE;
} else if (estNul()) {
actualState = GameState.CERCLE_BLEU_GAGNE;
}
}
public boolean estNul() {
/*for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
if (board[row][col] == Token.VIDE) {
return false;
}
}
}*/
return false;
}
public boolean aGagne(Token token, int ligneSelectionnee, int colonneSelectionnee) {
return false;
}
public void neighbours(int row, int col) {
for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1 ) {
for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1 ) {
if(!((colNum == col) && (rowNum == row))) {
if(withinGrid (rowNum, colNum )) {
System.out.println("Neighbor of "+ row + " " + col + " is " + rowNum +" " + colNum );
}
}
}
}
}
private boolean withinGrid(int colNum, int rowNum) {
if((colNum < 0) || (rowNum <0) ) {
return false;
}
if((colNum >= COLS) || (rowNum >= ROWS)) {
return false;
}
return true;
}
class DrawCanvas extends JPanel{
@Override
public void paintComponent(Graphics g){ //Invoqué via repaint()
super.paintComponent(g); //Pour remplir l'arriere plan
setBackground(Color.WHITE); //Defini la couleur de l'arriere plan
g.setColor(Color.BLACK);
for(int ligne = 1; ligne < ROWS; ++ligne){
g.fillRoundRect(0, cellSize * ligne - halfGridWidth, canvasWidth - 1,
gridWidth, gridWidth, gridWidth);
}
for(int colonne = 1; colonne < COLS; ++colonne){
g.fillRoundRect(cellSize * colonne - halfGridWidth, 0
, gridWidth, canvasHeight - 1,
gridWidth, gridWidth);
}
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke(symbolStrokeWidth,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
for(int ligne = 0; ligne < ROWS; ++ligne){
for(int colonne = 0; colonne < COLS; ++colonne){
int x1 = colonne * cellSize + cellPadding;
int y1 = ligne * cellSize + cellPadding;
if(board[ligne][colonne] == Token.CERCLE_ROUGE){
g2d.setColor(Color.RED);
g2d.drawOval(x1, y1, symbolSize, symbolSize);
g2d.fillOval(x1, y1, symbolSize, symbolSize);
} else
if(board[ligne][colonne] == Token.CERCLE_BLEU){
int x2 = colonne * cellSize + cellPadding;
g2d.setColor(Color.BLUE);
g2d.drawOval(x1, y1, symbolSize, symbolSize);
g2d.fillOval(x2, y1, symbolSize, symbolSize);
}
}
}
if(actualState == GameState.JOUE){
if(actualPlayer == Token.CERCLE_ROUGE){
statusBar.setText("ROUGE, c'est votre tour");
statusBar.setForeground(Color.RED);
} else {
statusBar.setText("BLEU, c'est votre tour");
statusBar.setForeground(Color.BLUE);
statusBar.addMouseMotionListener(null);
}
} else
if(actualState == GameState.NUL){
statusBar.setForeground(Color.yellow);
statusBar.setText("Match nul! Cliquez pour rejouer");
} else
if(actualState == GameState.CERCLE_ROUGE_GAGNE){
statusBar.setText("Le jouer X a remporté la partie, cliquez pour rejouer");
statusBar.setForeground(Color.RED);
} else
if(actualState == GameState.CERCLE_BLEU_GAGNE){
statusBar.setForeground(Color.BLUE);
statusBar.setText("Le joueur O a remporté la partie, cliquez pour rejouer");
}
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(() -> {
Pha pha = new Pha();
});
}
}
In board
array you store the Token
for each cell.
To retrieve this information you could change
System.out.println("Neighbor of "+ row + " " + col + " is " + rowNum +" " + colNum );
In neighbors()
to:
System.out.println("Neighbor of "+ row + " " + col + " is " + rowNum +" " + colNum +
" Contains "+ board[rowNum][colNum]);
Clicking the middle cell (17,19):
Prints out:
Neighbor of 17 19 is 16 18 Contains CERCLE_ROUGE
Neighbor of 17 19 is 17 18 Contains CERCLE_BLEU
Neighbor of 17 19 is 18 18 Contains CERCLE_ROUGE
Neighbor of 17 19 is 16 19 Contains CERCLE_BLEU
Neighbor of 17 19 is 18 19 Contains CERCLE_BLEU
Neighbor of 17 19 is 16 20 Contains CERCLE_ROUGE
Neighbor of 17 19 is 17 20 Contains CERCLE_BLEU
Neighbor of 17 19 is 18 20 Contains CERCLE_ROUGE
As mentioned in comments, consider creating a Model-View-Controller structure to the program, one that uses listeners who are notified of changes in state.
For example, you could use the same enum to represent cell state:
public enum Token {
VIDE, CERCLE_BLEU, CERCLE_ROUGE
}
Then the Model could contain a grid of Tokens, or if it needed something more complex, such as Token and neighbor count, you could create a Cell class that contained a Token plus a count of blue and rouge neighbors, and then have the model contain a grid of these. The Model must be able to accept listeners and then notify these listeners if its state changes. With Swing, I often use the Java beans SwingPropertyChangeSupport
for this, since you can add PropertyChangeListeners to this object, and then when you call its fireXxxx(...)
methods, all listeners will receive notification. You'll also need to give your class public methods that allow addition (and probably removal) of listeners. For example this simple model which creates a grid, adds listeners, allows getting and setting grid cell state, and notifies listeners of grid cell changes:
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
public class Model {
public static final int ROWS = 72;
public static final int COLS = ROWS;
public static final String TOKEN = "token";
private Token[][] grid = new Token[ROWS][COLS];
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
public Model() {
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
grid[r][c] = Token.VIDE;
}
}
}
public Token getToken(int row, int col) {
return grid[row][col];
}
public void setToken(Token token, int row, int col) {
Token oldValue = grid[row][col];
Token newValue = token;
grid[row][col] = token;
int index = row * grid[row].length + col;
pcSupport.fireIndexedPropertyChange(TOKEN, index, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(name, listener);
}
public int getRows() {
return ROWS;
}
public int getCols() {
return COLS;
}
}
The key method is this one:
public void setToken(Token token, int row, int col) {
Token oldValue = grid[row][col];
Token newValue = token;
grid[row][col] = token;
int index = row * grid[row].length + col;
pcSupport.fireIndexedPropertyChange(TOKEN, index, oldValue, newValue);
}
When a grid cell's token is set, it gets the old value and the new value and tells the property control support object to notify all listeners, passing in pertinent information. I sort of cheated and used the index property change so I can pass in an index that also holds the row and column number values, but I also could create a simple Java class to hold all pertinent new value information.
My View class also has a 2-dimensional grid or array, but here its a grid of JLabels, labels that hold ImageIcons, either a blue or red circle colored icon or a blank place-holder icon that is sized the same as the colored icons. I've given this class a Map that translates the Token state into the approprate ImageIcon, and have created 3 ImageIcons, one for each Token type.
I create my 2D grid, add the VIDE icon to each JLabel in the grid, and add a MouseListener to each JLabel. Note that the mouse listener does not change the JLabel's state, but rather all it does is notify listeners that a mouse press has occurred and where. The key here is to make the GUI or the view as dumb as possible.
The GUI also has methods that allow the control to change the icon displayed by an individual JLabel cell:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class View {
private static final int ICON_W = 12;
public static final String CELL_SELECTION = "cell selection";
private int rows;
private int cols;
private JPanel mainPanel = new JPanel();
private JLabel[][] grid;
private Map<Token, Icon> iconMap = new EnumMap<>(Token.class);
private int selectedRow;
private int selectedCol;
public View() {
iconMap.put(Token.VIDE, createIcon(new Color(0, 0, 0, 0)));
iconMap.put(Token.CERCLE_BLEU, createIcon(Color.BLUE));
iconMap.put(Token.CERCLE_ROUGE, createIcon(Color.RED));
}
private Icon createIcon(Color color) {
BufferedImage img = new BufferedImage(ICON_W, ICON_W, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(color);
g2.fillOval(1, 1, ICON_W - 2, ICON_W - 2);
g2.dispose();
return new ImageIcon(img);
}
public void createGrid(int rows, int cols) {
MyMouse myMouse = new MyMouse();
this.setRows(rows);
this.cols = cols;
mainPanel.setLayout(new GridLayout(rows, cols, 1, 1));
mainPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
mainPanel.setBackground(Color.BLACK);
grid = new JLabel[rows][cols];
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
grid[r][c] = new JLabel(iconMap.get(Token.VIDE));
grid[r][c].addMouseListener(myMouse);
grid[r][c].setOpaque(true);
grid[r][c].setBackground(Color.WHITE);
mainPanel.add(grid[r][c]);
}
}
}
public JPanel getMainPanel() {
return mainPanel;
}
public int getSelectedRow() {
return selectedRow;
}
public int getSelectedCol() {
return selectedCol;
}
public void setCell(Token token, int row, int col) {
grid[row][col].setIcon(iconMap.get(token));
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
private class MyMouse extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
JLabel selection = (JLabel) e.getSource();
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
if (selection == grid[r][c]) {
selectedRow = r;
selectedCol = c;
int index = r * grid[r].length + c;
mainPanel.firePropertyChange(CELL_SELECTION, -1, index);
}
}
}
}
}
}
The Controller class tries to put it all together. It gets a model and a view and hooks them together with listeners. The view listener listens for mouse clicks, and then changes the state of the model. The model listener listens for changes to its state, and then changes what the view displays.
import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
public class Control {
private Model model;
private View view;
public Control(Model model, View view) {
this.model = model;
this.view = view;
view.createGrid(model.getRows(), model.getCols());
view.getMainPanel().addPropertyChangeListener(new ViewListener());
model.addPropertyChangeListener(Model.TOKEN, new ModelListener());
}
private class ViewListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(View.CELL_SELECTION)) {
int row = view.getSelectedRow();
int col = view.getSelectedCol();
Token token = model.getToken(row, col);
if (token == Token.VIDE) {
token = Token.CERCLE_BLEU;
} else if (token == Token.CERCLE_BLEU) {
token = Token.CERCLE_ROUGE;
} else {
token = Token.VIDE;
}
model.setToken(token, row, col);
}
}
}
private class ModelListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
IndexedPropertyChangeEvent iEvt = (IndexedPropertyChangeEvent)evt;
int index = iEvt.getIndex();
int row = index / Model.COLS;
int col = index % Model.COLS;
Token token = (Token) evt.getNewValue();
view.setCell(token, row, col);
}
}
public void start() {
JFrame frame = new JFrame("MVC");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(view.getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Finally the main class to get everything started
import javax.swing.SwingUtilities;
public class PhaMvcMain {
public static void main(String[] args) {
// run all on the Swing event thread
SwingUtilities.invokeLater(() -> {
Model model = new Model();
View view = new View();
Control control = new Control(model, view);
control.start();
});
}
}
来源:https://stackoverflow.com/questions/43824645/how-to-obtain-the-state-of-grid-cells-based-on-a-specific-location-on-a-2d-array