Search code examples
javaswingjpanelpaintcomponentrepaint

How does repaint() work when I have multiple JPanel objects in a single JFrame?


I have read that when a JPanel object (or any instance of a class that extends JPanel) is part of a JFrame, each time the JVM thinks that the JFrame needs to be refreshed, the JPanel instance's paintComponent() method is called.

But what happens when I have two such objects, that are instances of two different classes? Running the code I've provided at the end, I found out that both paintComponent() methods are called, when I minimize, change the size or press the colourButton.

However, this is not the case when I press labelButton. It only invokes the MyDrawPanel paintComponent(). Why is that the case?

Thank you in advance!

class GUI {

    JFrame frame;
    JLabel label;

    void go() {
        JButton labelButton = new JButton("Click me to change that (<-) text");
        labelButton.addActionListener(new LabelListener());
        JButton colourButton = new JButton("Click me to change the colour");
        colourButton.addActionListener(new ColourListener());

        label = new JLabel("Don't change me!");

        frame = new JFrame();
        frame.setSize(600, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

        MyDrawPanel Q = new MyDrawPanel();
        DrawPanel P = new DrawPanel();

        frame.getContentPane().add(BorderLayout.CENTER, Q);
        frame.getContentPane().add(BorderLayout.EAST, labelButton);
        frame.getContentPane().add(BorderLayout.WEST, label);
        frame.getContentPane().add(BorderLayout.SOUTH, colourButton);
        frame.getContentPane().add(BorderLayout.NORTH, P);

    }

    class LabelListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event){
            label.setText("You've changed me!");
        }
    }

    class ColourListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event){
            MyDrawPanel.red = (int) (Math.random() * 255);
            MyDrawPanel.green = (int) (Math.random() * 255);
            MyDrawPanel.blue = (int) (Math.random() * 255);
            frame.repaint(); 
        }
    }
}

class MyDrawPanel extends JPanel {

    static int red = (int) (Math.random() * 255);
    static int green = (int) (Math.random() * 255);
    static int blue = (int) (Math.random() * 255);

    @Override
    public void paintComponent(Graphics g){
        Color randomColour = new Color(red, green, blue);

        g.setColor(randomColour);

        g.fillOval(70, 70, 75, 75);

        System.out.println("Q");
    }
}

class DrawPanel extends JPanel {

    @Override
    public void paintComponent(Graphics g){

        System.out.println("P");

    }
}

Solution

  • frame.repaint(); 
    

    This tells the frame to repaint itself and all its children. So all the components on the frame are repainted.

    label.setText("You've changed me!");
    

    The setText() method will invoke revalidate() and repaint() on the label. The repaint() tells the label to repaint itself and all of its children.

    The revalidate() will invoke the layout manager in case the size of any components have changed. In this case it looks like the label will get larger. This means the panel added to the center (your DrawPanel) will get smaller, so it also needs to be repainted.

    The components in the NORTH/SOUTH are not affected by the change in the labels size so they are not repainted.

    So Swing will only repaint whatever is necessary to minimize the painting.