Search code examples
javajframeconways-game-of-life

How to make a bunch of clickable panels in Java JFrame


I am trying to recreate the Game of Life in Java using JFrame. I have already completed most of the program but this one thing is bugging me. How do I make a bunch of fields(panels) which are clickable, so that the user can input their own pattern, instead of the computer randomly generating the pattern each time?


Solution

  • You could use a GridLayout layout manager to put all the JPanels in a grid, and for each JPanel, add an instance of the MouseAdapter class with addMouseListener() to listen for mouse clicks to flip their state. The instance of MouseAdapter would override mouseClicked() and within that function, flip the state of the JPanel.

    This is just to make a complete example, but here would be the creation of the frame and setting its layout manager:

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        int width = 200, height = 200;
        frame.setSize(width, height);
        int rows = width/10, cols = height/10;
        frame.setLayout(new GridLayout(rows, cols));
        // add all the cells
        for(int j = 0; j < cols; j++) {
            for(int i = 0; i < rows; i++) {
                frame.add(new Cell(i, j));
            }
        }
        frame.setVisible(true);
    }
    

    Then for each cell, we have instances of this class:

    class Cell extends JPanel {
    int row, col;
    public static final int STATE_DEAD = 0;
    public static final int STATE_ALIVE = 1;
    int state = STATE_DEAD;
    
    public Cell(int row, int col) {
        this.row = row;
        this.col = col;
        // MouseAdapter tells a component how it should react to mouse events
        MouseAdapter mouseAdapter = new MouseAdapter() {
            // using mouseReleased because moving the mouse slightly
            // while clicking will register as a drag instead of a click
            @Override
            public void mouseReleased(MouseEvent e) {
                flip();
                repaint(); // redraw the JPanel to reflect new state
            }
        };
        // assign that behavior to this JPanel for mouse button events
        addMouseListener(mouseAdapter);
    }
    
    // Override this method to change drawing behavior to reflect state
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // fill the cell with black if it is dead
        if(state == STATE_DEAD) {
            g.setColor(Color.black);
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }
    
    public void flip() {
        if(state == STATE_DEAD) {
            state = STATE_ALIVE;
        } else {
            state = STATE_DEAD;
        }
    }
    

    }

    Alternatively, you could override the paintComponent() method of one JPanel, and do the above but use addMouseMotionListener() as well, that way your one panel can track which drawn grid cell the mouse is in, and you can control how they are drawn.