So the situation is that I'm making a panel filled with 8*8 buttons, like a matrix, and I'm gonna have to change specific button texts depending on the position of the clicked button. It seemd like a good idea to make my own JButton where I can assign x and y indexes to the button, so I can check easily wich button was clicked by indexes.
Here is the code for that:
import javax.swing.JButton;
public class MatrixButton extends JButton {
private int x;
private int y;
public MatrixButton(int x, int y, String text){
super(text);
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
And it does solve the task, but these Matrix buttons doesn't want to appear until I mouse over them. What could be the problem?
here is the code for the panel wich contains these buttons and handles their actions:
public class PlayField extends JPanel implements ActionListener {
private MatrixButton[][] button = new MatrixButton[8][8];
public PlayField(){
Random rand = new Random();
setLayout(new GridLayout(8, 8));
for (int i = 0; i < 8; ++i){
for (int j = 0; j < 8; ++j){
button[i][j] = new MatrixButton(j, i, "" + rand.nextInt(80));
button[i][j].addActionListener(this);
add(button[i][j]);
}
}
}
private String incrementText(MatrixButton mb){
return "" + (Integer.parseInt(mb.getText()) + 1);
}
#Override
public void actionPerformed(ActionEvent e){
MatrixButton mb = (MatrixButton)e.getSource();
for (int i = mb.getY(); i >= 0; --i){
button[i][mb.getX()].setText(incrementText(button[i][mb.getX()]));
}
for (int i = 0; i < 8; ++i){
if (i != mb.getX())
button[mb.getY()][i].setText(incrementText(button[mb.getY()][i]));
}
}
}
PS: if I fill with ordinary JButtons they appear as they should be. Thats why I'm confused, cuz I didn't change much on the JButton extension just added 2 more variables to it.
This is dangerous code:
public int getX() {
return x;
}
public int getY() {
return y;
}
You do realize that this overrides two of JButton's critical methods (actually, the methods are from JButton's parent, JComponent) that help set the placement of the button on the GUI (add #Override above the methods to see that this is so). I strongly suggest that you either change the signature of these methods, perhaps getGridX() and getGridY(), or even better, use composition and not inheritance, since you run into risk of these types of problems -- unwittingly overriding a key method -- when you extend complex classes such as Swing component classes. For this reason I try to avoid extending these classes unless absolutely necessary.
For example:
import java.awt.GridLayout;
import java.awt.event.*;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PlayField {
private static final int ROWS = 8;
private static final int COLS = ROWS;
private static final int MAX_RAND = 80;
private JPanel mainPanel = new JPanel();
private MatrixButton[][] button = new MatrixButton[ROWS][COLS];
public PlayField(){
Random rand = new Random();
mainPanel.setLayout(new GridLayout(ROWS, COLS));
for (int i = 0; i < ROWS; ++i){
for (int j = 0; j < COLS; ++j){
button[i][j] = new MatrixButton(j, i, rand.nextInt(MAX_RAND));
button[i][j].addActionListener(new MyMatrixListener(i, j));
mainPanel.add(button[i][j].getButton());
}
}
}
private class MyMatrixListener implements ActionListener {
private int i;
private int j;
public MyMatrixListener(int i, int j) {
this.i = i;
this.j = j;
}
#Override
public void actionPerformed(ActionEvent e) {
for (int i2 = 0; i2 < button.length; i2++) {
if (i2 != i) {
int value = button[i2][j].getValue();
value++;
button[i2][j].setValue(value);
}
}
for (int j2 = 0; j2 < button[i].length; j2++) {
if (j2 != j) {
int value = button[i][j2].getValue();
value++;
button[i][j2].setValue(value);
}
}
}
}
public JPanel getMainPanel() {
return mainPanel;
}
private static void createAndShowGui() {
JFrame
frame = new JFrame("PlayField");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new PlayField().getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MatrixButton {
private int column;
private int row;
private JButton button = new JButton();
private int value;
public MatrixButton(int column, int row, int value) {
this.column = column;
this.row = row;
setValue(value);
}
public void addActionListener(ActionListener listener) {
button.addActionListener(listener);
}
public int getColumn() {
return column;
}
public int getRow() {
return row;
}
public JButton getButton() {
return button;
}
public int getValue() {
return value;
}
public final void setValue(int value) {
this.value = value;
button.setText("" + value);
}
}
Better to just use ints and not Strings
Related
I am trying to make a button, that upon clicked, changes the grid color of the rectangles I have drawn.
Here is my drawn grid. I would like the menu items I have under grid, when pressed, change the rectangle outline colors of the drawn grid. I have tried a couple methods, but seem to have trouble implementing it.
Here is my Menu Class
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyMenu
extends MenuBar
implements ActionListener {
Menu paperMenu;
MenuItem c1, c2, c3;
GUI gui;
Cell cell;
public MyMenu(GUI gui) {
this.gui = gui;
this.cell = cell;
paperMenu = new Menu("Grid");
c1 = new MenuItem("Red");
c2 = new MenuItem("Green");
c3 = new MenuItem("Random");
paperMenu.add(c1);
paperMenu.add(c2);
paperMenu.add(c3);
this.add(paperMenu);
c1.addActionListener(this);
c2.addActionListener(this);
c3.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource()==c1) {
}
}
}
Here is my Board class
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.*;
public class Board
extends Canvas {
private Cell[][] array;
public Board(int rows, int cols, int size) {
array = new Cell[rows][cols];
for (int r = 0; r < rows; r++) {
int y = r * size;
for (int c = 0; c < cols; c++) {
int x = c * size;
array[r][c] = new Cell(x, y, size);
}
}
setSize(cols*size,rows*size);
}
public void draw(Graphics g) {
for (Cell[] row : array) {
for (Cell cell : row) {
cell.paint(g);
}
}
}
public Cell getCell(int r, int c) {
return array[r][c];
}
public void paint(Graphics g) {
draw(g);
}
}
And Finally, here is my Cell Class
import java.awt.Color;
import java.awt.Graphics;
import java.awt.*;
public class Cell
{
private final int x;
private final int y;
private final int size;
int rows;
int cols;
Cell[][] array = new Cell[rows][cols];
public Color currentcolor = Color.BLACK;
public Cell(int x, int y, int size) {
this.x = x;
this.y = y;
this.size = size;
}
public void fillArray() {
for (int r = 0; r < rows; r++) {
int y = r * size;
for (int c = 0; c < cols; c++) {
int x = c * size;
array[r][c] = new Cell(x, y, size);
}
}
}
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.drawRect(x, y, size, size);
}
}
I obviously need to make it, so when the button is pressed, the g.setColor under the Cell class and paint method is changed and then repainted, but I am unsure on how to do this?
First, you might want to understand the difference between Swing and AWT (and why you might want to prefer the latter)
Understanding the Difference Between AWT And Swing In Java
Difference between AWT and Swing in Java
What is the difference between Swing and AWT?
What is the Difference Between AWT and Swing in Java
Difference Between AWT and Swing in Java
Difference between AWT and Swing
AWT and Swing in Java
If you're just starting out, you might also consider using JavaFX instead.
You will also want to investigate things like:
Dependency Injection
Composition over inheritance
Liskov substitution principle
Single Responsibility Principle
As this will all effect the way in which you approach designing your solutions to your problems.
For example, do you really need to extend from MenuBar? What new functionality are you bringing to the class, which couldn't be achieved through a builder or factory pattern?
Each individual menu item could also have it's action handling isolating, so that you're dealing with single response for each action, rather then, what will become, a messy if-else statement in the future (ie Single Responsibility Principle)
Having said all that, your basic problem comes down to, providing some way to notify the Board that it needs to update the gridColor when a menu item is selected.
This can be solved through the use a Observer Pattern. You've already used this, ActionListener is a implementation of the observer pattern.
Now, there is no reason for MyMenu to have full control over GUI (which I'm assuming is some kind Frame), it would be better to make some kind of delegate which is responsible for coordinating messaging between the menu bar and the interested party (this can some times be referred to as a controller, but I'm not going that far).
The first thing we need is to define the functionality we're willing to expose, for example...
public interface BoardConfigurable {
public void setBoardGridColor(Color color);
}
This is a simple contract for telling the interested party that the board grid color should be changed.
(Before some jumps down my throat, we could have a interface called, I don't, MyMenuListener, which then implements BoardConfigurable and possibly a bunch of other interfaces, which further allows for decoupling of the basic code, but this is what I would consider separating the action handling into isolated units of work - but this is going way beyond what is required right now)
We then update MyMenu to accept an instance of this delegate/observer and update the actionPerformed method to call it when ever an appropriate action is triggered.
public class MyMenu
extends MenuBar
implements ActionListener {
Menu paperMenu;
MenuItem c1, c2, c3;
BoardConfigurable boardConfig;
Cell cell;
public MyMenu(BoardConfigurable boardConfig) {
this.boardConfig = boardConfig;
this.cell = cell;
paperMenu = new Menu("Grid");
c1 = new MenuItem("Red");
c2 = new MenuItem("Green");
c3 = new MenuItem("Random");
paperMenu.add(c1);
paperMenu.add(c2);
paperMenu.add(c3);
this.add(paperMenu);
c1.addActionListener(this);
c2.addActionListener(this);
c3.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == c1) {
boardConfig.setBoardGridColor(Color.RED);
} else if (e.getSource() == c2) {
boardConfig.setBoardGridColor(Color.GREEN);
} else if (e.getSource() == c3) {
boardConfig.setBoardGridColor(Color.BLUE);
}
}
}
We then need to update the Board (and Cell) to accept this change
public class Board extends Canvas {
private Cell[][] array;
private int rows;
private int cols;
private int size;
private Color gridColor = Color.BLACK;
public Board(int rows, int cols, int size) {
this.rows = rows;
this.cols = cols;
this.size = size;
array = new Cell[rows][cols];
for (int r = 0; r < rows; r++) {
int y = r * size;
for (int c = 0; c < cols; c++) {
int x = c * size;
array[r][c] = new Cell(x, y, size);
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(cols * size, rows * size);
}
public Color getGridColor() {
return gridColor;
}
public void setGridColor(Color gridColor) {
this.gridColor = gridColor;
repaint();
}
public void draw(Graphics g) {
for (Cell[] row : array) {
for (Cell cell : row) {
cell.paint(g, getGridColor());
}
}
}
public Cell getCell(int r, int c) {
return array[r][c];
}
public void paint(Graphics g) {
super.paint(g);
draw(g);
}
}
public class Cell {
private final int x;
private final int y;
private final int size;
public Cell(int x, int y, int size) {
this.x = x;
this.y = y;
this.size = size;
}
public void paint(Graphics g, Color gridColor) {
g.setColor(gridColor);
g.drawRect(x, y, size, size);
}
}
note: Cell is making use of dependency injection here, as it really doesn't need to store the color AGAIN. It's also possible that the size could be passed this way, but, it might be useful for determine if a user clicked this cell or not
Then GUI needs to implement BoardConfigurable...
public class GUI extends Container implements BoardConfigurable {
private Board board;
public GUI() {
setLayout(new BorderLayout());
board = new Board(3, 3, 50);
add(board);
}
#Override
public void setBoardGridColor(Color color) {
board.setGridColor(color);
}
}
And we can update build and run the UI...
GUI gui = new GUI();
MyMenu menu = new MyMenu(gui);
Frame frame = new Frame();
frame.add(gui);
frame.setMenuBar(menu);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
An important skill to build is always questioning, "does this really need access to everything I'm passing?", "what will happen if I want to change the underlying implementation? How much will I need to change to achieve it?"
These will help you drive your designs and help build more flexible and decoupled code.
You might also want to become fairmular with the model-view-controller concept
Runnable example...
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
GUI gui = new GUI();
MyMenu menu = new MyMenu(gui);
Frame frame = new Frame();
frame.add(gui);
frame.setMenuBar(menu);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public interface BoardConfigurable {
public void setBoardGridColor(Color color);
}
public class GUI extends Container implements BoardConfigurable {
private Board board;
public GUI() {
setLayout(new BorderLayout());
board = new Board(3, 3, 50);
add(board);
}
#Override
public void setBoardGridColor(Color color) {
board.setGridColor(color);
}
}
public class MyMenu
extends MenuBar
implements ActionListener {
Menu paperMenu;
MenuItem c1, c2, c3;
BoardConfigurable boardConfig;
Cell cell;
public MyMenu(BoardConfigurable boardConfig) {
this.boardConfig = boardConfig;
this.cell = cell;
paperMenu = new Menu("Grid");
c1 = new MenuItem("Red");
c2 = new MenuItem("Green");
c3 = new MenuItem("Random");
paperMenu.add(c1);
paperMenu.add(c2);
paperMenu.add(c3);
this.add(paperMenu);
c1.addActionListener(this);
c2.addActionListener(this);
c3.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == c1) {
boardConfig.setBoardGridColor(Color.RED);
} else if (e.getSource() == c2) {
boardConfig.setBoardGridColor(Color.GREEN);
} else if (e.getSource() == c3) {
boardConfig.setBoardGridColor(Color.BLUE);
}
}
}
public class Board extends Canvas {
private Cell[][] array;
private int rows;
private int cols;
private int size;
private Color gridColor = Color.BLACK;
public Board(int rows, int cols, int size) {
this.rows = rows;
this.cols = cols;
this.size = size;
array = new Cell[rows][cols];
for (int r = 0; r < rows; r++) {
int y = r * size;
for (int c = 0; c < cols; c++) {
int x = c * size;
array[r][c] = new Cell(x, y, size);
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(cols * size, rows * size);
}
public Color getGridColor() {
return gridColor;
}
public void setGridColor(Color gridColor) {
this.gridColor = gridColor;
repaint();
}
public void draw(Graphics g) {
for (Cell[] row : array) {
for (Cell cell : row) {
cell.paint(g, getGridColor());
}
}
}
public Cell getCell(int r, int c) {
return array[r][c];
}
public void paint(Graphics g) {
super.paint(g);
draw(g);
}
}
public class Cell {
private final int x;
private final int y;
private final int size;
public Cell(int x, int y, int size) {
this.x = x;
this.y = y;
this.size = size;
}
public void paint(Graphics g, Color gridColor) {
g.setColor(gridColor);
g.drawRect(x, y, size, size);
}
}
}
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:
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.
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!
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].
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:
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;
}
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,
your view, here the drawing JPanel, should contain a reference to the model object via a has-a or "composition" structure
The view should be notified of changes in the model, often via event listeners such as a PropertyChangeListener
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);
}
}
We are learning in school java graphic and now we have to build a game called Kakurasu (https://www.brainbashers.com/showkakurasu.asp)
I'm so stuck and I don't know how to do it.
As far as I came. I've done a frame with a panel inside a panel which has 7x7 buttons that change from 0 to 1 and from white to green when pressed. The idea is that I make the first raw on the top and on the left to be the numbers 1-5 and then on the bottom and right side the random generated numbers.
This is my first code:
import javax.swing.*;
import java.awt.*;
public class Start {
public static JButton[][] gumbi;
public static void main(String[] args) {
buttons = new JButton[7][7];
JFrame window = new JFrame("Kakurasu");
JPanel panel = new JPanel(new BorderLayout());
JPanel playingField = new JPanel(new GridLayout(7, 7));
Listner1 p = new Listner1(buttons);
panel.add(playingField, BorderLayout.CENTER);
window.add(panel);
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 7; j++) {
buttons[i][j] = new JButton("0");
playingField.add(buttons[i][j], BorderLayout.CENTER);
buttons[i][j].addActionListener(p);
buttons[i][j].setBackground(Color.WHITE);
}
}
window.setVisible(true);
window.setSize(500, 500);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
This is my second code:
import javax.swing.*;
import java.awt.*;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Poslusalec1 implements ActionListener {
public JButton[][] buttons;
public Listner1(JButton[][] gumbi) {
this.buttons = buttons;
}
public void actionPerformed(ActionEvent e) {
JButton button = (JButton) e.getSource();
String tmp = button.getText();
int n = Integer.parseInt(tmp);
n += 1;
if (n == 2) {
n = 0;
}
button.setText("" + n);
if (button.getBackground() == Color.WHITE) {
button.setBackground(Color.GREEN);
} else {
button.setBackground(Color.WHITE);
}
for (int i = 0; i < buttons.length; i++) {
for (int j = 0; j < buttons[i].length; j++) {
if (button == buttons[i][j]) {
System.out.println(i + ", " + j);
}
}
}
}
}
Is it possible to assign different actionlisteners to different buttons inside a gridlayout?
Thanks for any help.
I'm so stuck and I don't know how to do it.
To solve any computer problem, you break the problem down into smaller and smaller problems, until you're comfortable that you can code each small problem.
Generally, when coding a GUI, you should use the model / view / controller pattern.
In Java Swing, this means:
The view may read values from the model.
The view may not update the model.
The controller updates the model.
The controller repaints / revalidates the view.
So, let's create a model for a JButton. The model will hold the across value of the JButton, the down value of the JButton, and the background color of the JButton.
package com.ggl.testing;
import java.awt.Color;
public class KakurasuCell {
private final int acrossValue;
private final int downValue;
private Color backgroundColor;
public KakurasuCell(int acrossValue, int downValue, Color backgroundColor) {
this.acrossValue = acrossValue;
this.downValue = downValue;
this.backgroundColor = backgroundColor;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
public int getAcrossValue() {
return acrossValue;
}
public int getDownValue() {
return downValue;
}
}
This is a Java object. It holds multiple types of values.
Now, we create another model for a grid of JButtons. You should recognize this from your code.
package com.ggl.testing;
import java.awt.Color;
public class KakurasuGrid {
private int gridWidth;
private KakurasuCell[][] cells;
public KakurasuGrid(int gridWidth) {
setGridWidth(gridWidth);
}
public int getGridWidth() {
return gridWidth;
}
public void setGridWidth(int gridWidth) {
this.gridWidth = gridWidth;
this.cells = new KakurasuCell[gridWidth][gridWidth];
setCells();
}
public KakurasuCell[][] getCells() {
return cells;
}
private void setCells() {
for (int i = 0; i < gridWidth; i++) {
for (int j = 0; j < gridWidth; j++) {
KakurasuCell cell = new KakurasuCell((j + 1), (i + 1),
Color.GRAY);
cells[i][j] = cell;
}
}
}
}
This should be enough to get you started. You still need to create the answers, create the GUI, and add the controller methods.
When I set X and Y values for my array of JButtons, I get back the correct values only multiplied by 93. I can solve the problem by dividing the value by 93 but I would rather find out where the bug was in the first place.
I have two classes in the code, one for the actual program, and one for the button object along with the coordinates.
Here's the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.GridLayout;
import java.io.*;
public class ConnectFour implements ActionListener
{
JFrame frame = new JFrame();
Button [][] buttons = new Button[6][7];
public ConnectFour()
{
frame.setSize(700,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(6,7));
frame.setLocationRelativeTo(null);
frame.setTitle("Connect Four");
for(int filler = 0; filler <= 5; filler++)
{
for(int filler2 = 0; filler2 <= 6; filler2++)
{
buttons[filler][filler2] = new Button();
buttons[filler][filler2].setX(filler2);
buttons[filler][filler2].setY(filler);
//System.out.println(buttons[filler][filler2].getX());
//System.out.print(buttons[filler][filler2].getY());
frame.add(buttons[filler][filler2].button);
buttons[filler][filler2].button.addActionListener(this);
}
}
frame.setVisible(true);
}
public void actionPerformed(ActionEvent a)
{
JButton pressedButton = (JButton)a.getSource();
System.out.print(pressedButton.getY() / 93);
System.out.print(pressedButton.getX() / 93);
}
public static void main(String args[])
{
ConnectFour gameplay = new ConnectFour();
}
}
Here's the Button class:
import javax.swing.JButton;
public class Button
{
JButton button;
private int x = 0;
private int y = 0;
public Button()
{
button = new JButton();
}
public int getX() {return x;}
public int getY() {return y;}
public void setX(int xIndex)
{
x = xIndex;
}
public void setY(int yIndex)
{
y = yIndex;
}
}
You're mixing your two Button classes.
In this line, you're adding an actionListener to Button.button:
buttons[filler][filler2].button.addActionListener(this);
because JButton also has methods getX and getY, you can call them. When you do:
pressedButton.getX()
you're getting the x position of the JButton, not of your Button.
What I think would be the easiest way to solve this problem is making your button extend JButton and rename x and y to row and column, for instance:
public class Button extends JButton {
private int row = 0;
private int column = 0;
public Button(int row, int column) {
super();
this.row = row;
this.column = column;
}
public int getRow() {return row;}
public int getColumn() {return column;}
}
You can create you buttons as
for(int filler = 0; filler <= 5; filler++) {
for(int filler2 = 0; filler2 <= 6; filler2++) {
buttons[filler][filler2] = new Button(filler2, filler);
frame.add(buttons[filler][filler2]);
buttons[filler][filler2].addActionListener(this);
}
}
And use them in the ActionListener as
public void actionPerformed(ActionEvent a) {
Button pressedButton = (Button)a.getSource();
System.out.print(pressedButton.getColumn());
System.out.print(pressedButton.getRow());
}
I could not figure how to do it using your composition example, but this one works as you might want.
import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.GridLayout;
import java.io.*;
public class ConnectFour implements ActionListener
{
JFrame frame = new JFrame();
CustomButton [][] buttons = new CustomButton[6][7];
public ConnectFour()
{
frame.setSize(700,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(6,7));
frame.setLocationRelativeTo(null);
frame.setTitle("Connect Four");
for(int filler = 0; filler <= 5; filler++)
{
for(int filler2 = 0; filler2 <= 6; filler2++)
{
buttons[filler][filler2] = new CustomButton(filler,filler2);
frame.add(buttons[filler][filler2]);
buttons[filler][filler2].addActionListener(this);
}
}
frame.setVisible(true);
}
public void actionPerformed(ActionEvent a)
{
CustomButton pressedButton = (CustomButton)a.getSource();
System.out.println(pressedButton.getRow() + "/" + pressedButton.getCol());
}
public static void main(String args[])
{
ConnectFour gameplay = new ConnectFour();
}
}
class CustomButton extends JButton
{
private int row = 0;
private int col = 0;
public CustomButton(int row, int col)
{
this.row = row;
this.col = col;
}
public int getRow() {return row;}
public int getCol() {return col;}
public void setRow(int row)
{
this.row = row;
}
public void setCol(int col)
{
this.col = col;
}
}
I think x and y are defined in the superclass, try changing the variable names to something else. e.g.
private int myX = 0;
private int myY = 0;
I'm planning on making an editor for my current project and will need some java swing for it.
Basically I need a grid within some sort of frame; the single elements of the grid should be able to be selected via click and with a dropdown/selector element you should be able to change the color of the grid element
Can anyone tell me what parts of swing I'll need for that? Any help would be really appreciated ;)
Edit: let's go a bit into detail
This editor is planned to generate maps for an android strategy game I develope with some friends of mine
Let's say we have a square field of 16x16 fields which are all by default green.
By selecting the single field I want to be able to change the color of this field to something else.
In addition, every field should be able to return it's value (i.e. the color)
Your question is a little short on details, but perhaps you want a JPanel that uses GridLayout and holds an array of JLabels whose opaque property is true. You could add a MouseListener to the JLabels that shows a JPopupMenu that shows possible colors, and then depending on the selection use it to set the JLabel's background color (which shows since it has been made opaque).
For example:
Main.java
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main {
private static void createAndShowGui() {
int rows = 20;
int cols = 40;
int cellWidth = 20;
ColorGrid mainPanel = new ColorGrid(rows, cols, cellWidth);
JFrame frame = new JFrame("Color Grid Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
ColorGrid.java
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.*;
#SuppressWarnings("serial")
public class ColorGrid extends JPanel {
private MyColor[][] myColors;
private JLabel[][] myLabels;
public ColorGrid(int rows, int cols, int cellWidth) {
myColors = new MyColor[rows][cols];
myLabels = new JLabel[rows][cols];
MyMouseListener myListener = new MyMouseListener(this);
Dimension labelPrefSize = new Dimension(cellWidth, cellWidth);
setLayout(new GridLayout(rows, cols));
for (int row = 0; row < myLabels.length; row++) {
for (int col = 0; col < myLabels[row].length; col++) {
JLabel myLabel = new JLabel();
myLabel = new JLabel();
myLabel.setOpaque(true);
MyColor myColor = MyColor.GREEN;
myColors[row][col] = myColor;
myLabel.setBackground(myColor.getColor());
myLabel.addMouseListener(myListener);
myLabel.setPreferredSize(labelPrefSize);
add(myLabel);
myLabels[row][col] = myLabel;
}
}
}
public MyColor[][] getMyColors() {
return myColors;
}
public void labelPressed(JLabel label) {
for (int row = 0; row < myLabels.length; row++) {
for (int col = 0; col < myLabels[row].length; col++) {
if (label == myLabels[row][col]) {
MyColor myColor = myColors[row][col].next();
myColors[row][col] = myColor;
myLabels[row][col].setBackground(myColor.getColor());
}
}
}
}
}
MyColor.java
import java.awt.Color;
public enum MyColor {
GREEN(Color.green, "Green", "g"), RED(Color.red, "Red", "r"),
BLUE(Color.blue, "Blue", "b"), YELLOW(Color.yellow, "Yellow", "y");
private Color color;
private String name;
private String shortName;
private MyColor(Color color, String name, String shortName) {
this.color = color;
this.name = name;
this.shortName = shortName;
}
public MyColor next() {
int index = 0;
for (int i = 0; i < MyColor.values().length; i++) {
if (MyColor.values()[i] == this) {
index = (i + 1) % MyColor.values().length;
}
}
return MyColor.values()[index];
}
public Color getColor() {
return color;
}
public String getName() {
return name;
}
public String getShortName() {
return shortName;
}
#Override
public String toString() {
return shortName;
}
}
MyMouseListener.java
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JLabel;
public class MyMouseListener extends MouseAdapter {
private ColorGrid colorGrid;
public MyMouseListener(ColorGrid colorGrid) {
this.colorGrid = colorGrid;
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
colorGrid.labelPressed((JLabel)e.getSource());
} else if (e.getButton() == MouseEvent.BUTTON3) {
MyColor[][] myColors = colorGrid.getMyColors();
for (int row = 0; row < myColors.length; row++) {
for (int col = 0; col < myColors[row].length; col++) {
System.out.print(myColors[row][col] + " ");
}
System.out.println();
}
System.out.println();
}
}
}