First, you might want to understand the difference between Swing and AWT (and why you might want to prefer the latter)
If you're just starting out, you might also consider using JavaFX instead.
You will also want to investigate things like:
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);
}
}
}