6

I'm trying to create a Java game with a 10 x 10 grid made up of cells. The Grid looks liks this:

public class Grid extends JPanel implements MouseListener {
    public static final int GRID_SIZE = 10;

    public Grid() {
        setPreferredSize(new Dimension(300, 300));
        setLayout(new GridLayout(GRID_SIZE, GRID_SIZE));

        for (int x = 0; x < GRID_SIZE; x++)
            for (int y = 0; y < GRID_SIZE; y++)
                add(new Cell(x, y));
        addMouseListener(this);
    }

// All Mouse Listener methods are in here.

The Cell class looks like this:

public class Cell extends JPanel {

    public static final int CELL_SIZE = 1;
    private int xPos;
    private int yPos;

    public Cell (int x, int y) {
        xPos = x;
        yPos = y;
        setOpaque(true);
        setBorder(BorderFactory.createBevelBorder(CELL_SIZE));
        setBackground(new Color(105, 120, 105));
        setPreferredSize(new Dimension(CELL_SIZE, CELL_SIZE));
    }

    // Getter methods for x and y.

My issue is that I am now trying to implement the MouseListeners in the Grid class. What I have realised is that whilst I can return the X and Y coordinates of the Grid, I can't seem to manipulate the cells themselves. I am assuming this is because when I created them in Grid, I am creating 100 random Cells with no identifiers and so I have no way to directly access them.

Could anybody give me advice on this? Do I need to overhaul my code and the way I am creating the cells? Am I being terribly stupid and missing an obvious way of accessing them? Thanks

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Andrew Martin
  • 5,619
  • 10
  • 54
  • 92

3 Answers3

2

You could use an adapter pattern, as shown below. That way, you can add the listener to each grid cell individually, but still handle the events from Grid.

Note that Grid no longer implements MouseListener, this is handled by the cells now.

public class Grid extends JPanel {
    public static final int GRID_SIZE = 10;

    public Grid() {
        setPreferredSize(new Dimension(300, 300));
        setLayout(new GridLayout(GRID_SIZE, GRID_SIZE));

        for (int x = 0; x < GRID_SIZE; x++) {
            for (int y = 0; y < GRID_SIZE; y++) {
                final Cell cell = new Cell(x, y);
                add(cell);
                cell.addMouseListener(new MouseListener() {
                    public void mouseClicked(MouseEvent e) {
                        click(e, cell);
                    }
                    // other mouse listener functions
                });
            }
        }        
    }

    public void click(MouseEvent e, Cell cell) {
        // handle the event, for instance
        cell.setBackground(Color.blue);
    }

    // handlers for the other mouse events
}

A subclass could override this as:

public class EnemyGrid extends Grid {
    public void click(MouseEvent e, Cell cell) {
        cell.setBackground(Color.red);
    }
}
Vincent van der Weele
  • 12,927
  • 1
  • 33
  • 61
  • This is a really useful piece of code - thank you! Can I ask a follow up question - I actually create two subclasses out of Grid: OwnGrid and EnemyGrid. They both originally overrode the MouseListener methods that were contained in the Grid class. With the new structure that you have proposed, how could I override these methods in the Grid subclasses? – Andrew Martin Apr 24 '13 at 13:23
  • @AndrewMartin I'm not sure if I understand that correctly, but if you want to handle the event differently in both subclasses, you can just override `mouseClicked(MouseEvent e, Cell cell)` – Vincent van der Weele Apr 24 '13 at 13:28
  • `how could I override these methods in the Grid subclasses` not this is not easy job(since is possible without any issue), you can to lost in JCOmponents hierarchy, one of disadvantage came from inheritance , [use composition instead](http://www.javaworld.com/jw-11-1998/jw-11-techniques.html) – mKorbel Apr 24 '13 at 13:33
  • What I meant was this: Let's say I follow your method and make the mouseClicked method set the background yellow. In the subclass, I created a method called public void mouseClicked, which accepts the same parameters - MouseEvent and Cell. However, in this method I try and change the background color to red. Unfortunately, nothing happens. What could I do to fix this? – Andrew Martin Apr 24 '13 at 13:34
  • @AndrewMartin I was a little unlucky with the name of the method. I renamed it to `click` and have no problem with overriding at all. – Vincent van der Weele Apr 24 '13 at 13:52
  • I followed the code down to adding the mouse listener, created the "new MouseListener()" and added all the necessary methods. I added two methods for each action. The first method "mouseClicked" overrides the MouseListener interface and calls the second method, sending the MouseEvent and cell as parameters. the second method ("click") takes both of these parameters and performs an action. In the subclass, I have the EXACT SAME second method ("click"), but it complains that it is not overriding anything and so doesn't do anything. – Andrew Martin Apr 24 '13 at 14:02
  • @AndrewMartin note that `click` is *not* inside the `MouseListener`, but is just a method of `Grid`. Moreover, I removed the `implements MouseListener` from `Grid` because it is no longer needed (sorry for not mentioning that :-) ) – Vincent van der Weele Apr 24 '13 at 14:09
  • Flipping eejit I am - I noticed the MouseListener not being implemented anymore, but had all my methods like click inside the MouseListener. As soon as I moved them outside, everything worked perfectly. You should probably make that totally explicit in your answer. Lots of idiots like me online these days! – Andrew Martin Apr 24 '13 at 14:10
  • On a final note. Thanks for this answer. I've been stuck on this for hours and you've solved it for me in minutes. Honestly, thanks a million. – Andrew Martin Apr 24 '13 at 14:11
  • @AndrewMartin tnx for the advise, I made the answer a little more elaborate. And yw of course :-) – Vincent van der Weele Apr 24 '13 at 14:18
2
Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Thanks for your answer and your comment on the other post. I am not really familiar with composition. I have gotten rid of the "extends" part of the subclass and have instead created done "private Grid grid = new Grid()" - how can I now connect this to mouseListeners etc? – Andrew Martin Apr 24 '13 at 13:37
  • [see another JButton and ActionListener](http://stackoverflow.com/a/9007348/714968), again change JButton to JPanel and ActionListener to MouseListener only, as general and working code example – mKorbel Apr 24 '13 at 13:41
  • I'd suggest to use putClientProperty for grid based games e.g. puzzle, sudoku, .... – mKorbel Apr 24 '13 at 13:46
  • Point taken. I'm trying to read up on it now. I've never used almost any of these constructs before! – Andrew Martin Apr 24 '13 at 13:46
1

The most obvious way would be to move your MouseListener on the Cell class itself.

Second option I can think of is to use java.awt.Container.getComponentAt(int, int).

Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117