Concerns about the function of JPanel: paintcomponent()

hello I'm new in java programming, and i need someone explaining me these lines of code:

 public class drawpanel extends JPanel
 {
     public void paintComponent(Graphics g)
     { 
         super.paintComponent(g);
         ...
     }
 }

I dont understand the line public void paintComponent(Graphics g): why do i have to declare that function like that if it's predefined in JPanel?

And this line super.paintComponent(g): I dont understand it at all. Thank you for your help.


Solution 1:

The Basic Structure:

The extends keyword means that DrawPanel inherits from JPanel. In other words, DrawPanel "is a" JPanel. As such, it can override its methods (the ones that aren't marked final). You might want to do that for several reasons. For example, you might want to gain access to the panel's Graphics class, which you could use to draw a circle on the panel, or a bar graph, or a string of text.

If you don't override any methods, then when you extend JPanel you get something like this:

public class DrawPanel extends JPanel {
    //TODO not much
}

However, that's not very useful ...unless you just don't like the name JPanel and want to call it AwesomePanel instead (note: don't do that). If that's all you have, you're better off just creating an instance of JPanel, like this: JPanel drawPanel = new JPanel();


paintComponent:

The purpose of extending a JPanel is to override the paintComponent method. The JPanel is invisible until you override paintComponent (note: being invisible is what makes it a useful container for buttons and other components). You are right that the paintComponent method is pre-defined (in the JComponent class if you were wondering), but all that method does is make an empty JPanel. If you want to draw something on the panel, then you need to override it, like this:

public class DrawPanel extends JPanel {
    @Override public void paintComponent(Graphics g) { // <-- HERE!
        //TODO draw stuff
    }
}

note: the @Override part is not strictly necessary but it's good practice to include it because it reduces the number runtime errors and improves code readability

You now have access to the Graphics object g for the panel. Graphics is a helper class that allows you to draw things on the panel, like this:

public class DrawPanel extends JPanel {
    @Override public void paintComponent(Graphics g) {
        g.drawOval(50, 50, 50, 50); // <-- draws an oval on the panel
    }
}

super.paintComponent:

helpful metaphor (that I just made up): The JPanel is the canvas, the Graphics object is your paintbrush, and super.paintComponent(g) is your eraser. (Also, JFrame is your easel.)

So super.paintComponent(g) invokes the paintComponent method from the superclass of JPanel (the JComponent class) to erase whatever is currently drawn on the panel. This is useful for animation.

For example, consider drawing an analog clock on the panel. You need to refresh it every second, so each second you have to erase the previous clock and redraw the clock, adjusting the second hand. If you don't invoke super.paintComponent(g) before redrawing the clock, it will just keep drawing new clocks on top of the old clocks and in 60 seconds what you'll have is just a filled in circle, more or less.

Only one more thing to remember: always call super.paintComponent(g) first in the paintComponent method, like this:

public class DrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        super.paintComponent(g); // <-- HERE!
        g.drawOval(50, 50, 50, 50);
    }
}

That's it. Feel free to contact me.


Example:

I created a simple example that uses these concepts to display a string of text on a panel (which is placed inside a frame). Save in your IDE as TestPanel.java.

import java.awt.*;
import java.util.*;
import javax.swing.*;

/**
 * A frame containing a panel that is sometimes red and sometimes 
 * blue. Also, it displays the word to go with it. 
 * 
 * Inspired by www.sometimesredsometimesblue.com.
 *
 */
public class TestPanel extends JPanel {

    private Random random = new Random();
    private boolean isRed;
    private String s = "";

    public TestPanel() {
        //randomly determine whether the background should be red
        isRed = random.nextBoolean();

        //set the background to blue
        setBackground(Color.BLUE);
        s = "BLUE";

        //if 'isRed' is true, set the background to red
        if (isRed) {
            setBackground(Color.RED);
            s = "RED";
        }
    }

    @Override 
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        //write either "RED" or "BLUE" using graphics
        g.setColor(Color.WHITE);
        g.setFont(new Font("serif", Font.BOLD, 60));
        g.drawString(s, getWidth() / 2 - g.getFontMetrics().stringWidth(s) / 2,
                getHeight() / 2 + g.getFontMetrics().getHeight() / 2);
    }

    //main method: create an instance of TestPanel and output it on a JFrame
    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setSize(500, 500);
        f.setTitle("Sometimes Red, Sometimes Blue");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setContentPane(new TestPanel());
        f.setVisible(true);
    }
}

Solution 2:

paintComponent() is a method originally defined in abstract class JComponent. Components that extend JComponent directly indirectly (if exposed) have the option to override paintComponent(). the call to super.paintComponent(g) calls the super class's implementation of paintComponent() (in your case it's JPanel's). You would want to override paintComponent() if you want to do other stuff with Graphics g besides what JPanel already does.