1

I have a Custom JPanel that does not call super.paintComponent() in its overridden paintComponent method. I know that this is in many situations not recommended, but I wonder why the following occures:

When I start two threads that each create a JFrame and Custom Panel, the Panels somehow mix their contents. This example program will create two Frames that each show a Panel with "ONE" and "TWO" as content. But I expected to have one frame with "ONE" and one frame with "TWO" as content.

public class CustomPanel extends JPanel implements Runnable {

public static void main(String[] args) {
    //create two threads constantly repainting the custom panel
    //expected two panels with different content
    //but got two panels with same content
    new Thread(new CustomPanel("ONE", 20)).start();
    new Thread(new CustomPanel("TWO", 40)).start();

}
/** creates a frame and adds the custom panel with specified text **/
public CustomPanel(String text, int y) {
    setPreferredSize(new Dimension(200,50));
    this.text = text;
    this.y = y;
    JFrame f = new JFrame(text);
    f.add(this);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.pack();
    f.setVisible(true);
}

private String text;//the text to draw
private int y;//where to draw

/** draw the component **/
@Override
public void paintComponent(Graphics g) {
    g.setColor(Color.red);
    g.drawString("Hello " + text, 50, y);//this line will be drawn twice on each panel (with text and y from the other)
    g.dispose();
}

/** constantly repaint the custom panels **/
@Override
public void run() {
    while (true) {
        this.repaint();
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

}

Result

Frame1                   Frame2
++++++++++++++++++     ++++++++++++++++++
+   Hello ONE    +     +   Hello ONE    + 
+   Hello TWO    +     +   Hello TWO    +
++++++++++++++++++     ++++++++++++++++++

Expected Result

Frame1                   Frame2
++++++++++++++++++     ++++++++++++++++++
+   Hello ONE    +     +                + 
+                +     +   Hello TWO    +
++++++++++++++++++     ++++++++++++++++++

So how on earth are two panels painted in the same manner?

Side note 1: call super.paint() in paint() will make the Custom Panels look different

Side note 2: Moving a frame two a different screen, will make its panel draw as expected (only its own text will be shown), whereas the other will remain the same.

Update This test was done on Windows 8.1 and jre 1.7

jannikb
  • 476
  • 1
  • 6
  • 16
  • Can't reproduce your observations (may be OS dependent?). Some other considerations a) create your UI on the EDT b) don't dispose of the Graphics object in the paint method. c) override `paintComponent` rather than `paint` – copeg May 02 '15 at 15:20
  • 1
    Possibly the back buffer gets reused (I can't reproduce it either). You don't obey the [opaqueness contract](http://stackoverflow.com/questions/2451990/setopaquetrue-false-java), so broken rendering is no surprise. – kiheru May 02 '15 at 16:23
  • You're not creating your UI on the EDT, so no wonder strange things happen. I suggest you check a book/tutorial on how to use Swing. It is not as simple as you might think at first (same happened to me btw). – m0skit0 May 02 '15 at 17:42
  • thanks for the comments. The opaque thing got me ( creating the UI stuff on the EDT did not solve the issue) – jannikb May 02 '15 at 17:55

2 Answers2

2

call setOpaque(false) on the custom panel or draw with the Graphics object the entire component content

The problem with the code is that it is not honoring the opaque property http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#paintComponent(java.awt.Graphics).

jannikb
  • 476
  • 1
  • 6
  • 16
  • The EDT is the Event Dispatch Thread which dispatches events to components and has nothing directly to do with painting other than schedule the request. Your solution will work but is not the proper solution (see my answer for the proper solution). It works because the painting logic will realize the component is transparent, so it looks at the parent component until it finds a component that is opaque, then it paints that parent so the background is painted properly and finally it paints the children. This is not very efficient. Don't use transparency unless you really need to. – camickr May 02 '15 at 18:51
  • the question was not about efficiency.. If the EDT only dispatches, what actually then figures the painting stuff out. I guess its part of the Swing framework? – jannikb May 03 '15 at 00:24
  • `the question was not about efficiency..` I didn't say it was. I was giving you another reason why your solution was not very good. You don't understand the full implication of your solution and I don't want other people to copy it. Just because something works doesn't mean you should use that approach, especially when there is a simple more common well know approach. `I guess its part of the Swing framework?` Read the article about [Painting in AWT and Swing](http://www.oracle.com/technetwork/java/painting-140037.html). – camickr May 03 '15 at 01:30
  • Read up more on the EDT from the Swing tutorial on [Concurrency](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html). All Swing events are added to the EDT where they are processed one at a time. – camickr May 03 '15 at 01:37
0

Custom painting is done by overriding the paintComponent() method (not paint()) and then you invoke super.paintComponent():

@Override
protected void paintComponent(Graphics g)
{
    super.paintComponent(g);
    // add your painting code
}

The super.paintComponent(g) makes sure the background of the component is painted with the background Color of the component. This clears the area of any garbage that might be there.

Edit:

You can also read the section from the Swing tutorial on A Closer Look at the Paint Mechanism you lose the functionality of those 3 methods.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • While this is in many situations the recommended approach, I explicitly asked for the situation when super.paint is not called (or super.paintComponent, does not matter here) – jannikb May 03 '15 at 00:27
  • I replaced paint with paintComponent in my question. calling super.paintComponent is however not the solution, as per definition of the question – jannikb May 03 '15 at 15:20
  • @yyannecc, I edited my answer to provide you with the answer to your question. Did you read the link I provided? Did you read the link I provided in your answer? Don't expect people to do the reading and summarize everything for you. You have been given the resources to answer your question so do the reading. If you have a specific question from the reading the ask a question and I will be glad to help, if I can. – camickr May 03 '15 at 18:31
  • yeah I read the links. The problem is that I dont want to call super.paintComponent, thats why I need to honor the opaque property http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#paintComponent(java.awt.Graphics). So either painting the whole component or setting opaque to false is the solution for me – jannikb May 03 '15 at 21:02