Why is paintComponent() continuously and asynchron

2019-07-19 19:33发布

问题:

So this question has two parts, which I think may be related, and it's mostly abstract. Briefly, here's what I'm doing:

I have a JFrame with a JPanel and some child JPanels each with 3 JButtons on it. I also created a JComponent called glassPanel for the JFrame (i.e. myJFrame.setGlassPane(glassPanel)), that allows me to paint over the JPanels and buttons.

(1) Essentially triggered by clicking all 3 buttons on a JPanel, glassPanel is set to Visible (which appears to then call paintComponent()). This is relates to the first question.

(2) In paintComponent() I draw and paint rectangles and images, using a double buffer, onto glassPanel. This is relates to the second question.

Here's my relevant GlassPanel class code (this is not an SSCCE because it is an abstract question for now):

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JComponent;


public class GlassPanel extends JComponent {

     @Override
     protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        setDoubleBuffered(true);
        Graphics2D g2 = (Graphics2D) g;

        g2.drawRect(x,y,width,height);
        g2.fillRect(x,y,width,height);

        g2.drawImage(img, x, y, this);
    }
}

By placing a System.out.print statement inside the paintComponent() method, I could tell it was being called continuously and also asynchronously. For how I think the call is made, see (1). Also, let's say I'm absolutely certain there is no call to repaint() anywhere in the code (I've checked many, many times). This is the basis of the first question.

The first time I click 3 buttons, all goes smoothly. The rectangle and images are both drawn immediately. However, when I click the next 3 buttons (at this point, glassPanel has already been setVisible(true) and the first rectangle and image are still on the screen, painted over the first 3 buttons), the second rectangle and image only load partially. When I click away from the JFrame and onto the Eclipse window that I've run the program from, the number of calls to paintComponent() rapidly increases by the same amount each time AND the partially loaded image(s) and rectangle(s) immediately and completely show up in the background JFrame. When I click back to the JFrame, the number of calls goes up again by an exact amount). This is the basis of the second question.

UPDATE: Here's something I read:

Also, when the GUI is covered by another window and then becomes uncovered, the painting system invokes the paintComponent method with the painting area equal to the newly uncovered area.

My questions are:

(1) why might paintComponent() be called so much without a repaint()? Or, a similar question, what might be calling paintComponent()?

UPDATE: After doing a little math, I strongly believe that it's being called by every component (all buttons and panels) continuously. But still, no call to repaint()...

(2) Why are the images loaded partially until i take focus from the JFrame window?

Note that I have tried many things: (a) creating my own doubleBuffer and using no double buffer (I know it's mostly for animation), (b) overriding and not overriding paintComponent(), (c) drawing and not drawing the image (the rectangle still takes time to load), (d) making absolutely sure there was no repaint(), (e) using and not using SwingUtilities.invokeLater(new Runnable() { public void run() { //stuff});, (f) done an if statement to only setVisible(true) once.

I can try and past the SSCCE if I must, but I do think these are more abstract. Thanks!

回答1:

Well, I think I've answered both questions. For the first, why paintComponent() was being called continuously, was that it was not actually being called continuously. It was being called by all the components when it first shows the GUI. When the Eclipse window covers it and then uncovers it, it gets called more times.

For the second, it has to do with the clipBounds of the Graphics2D object/thing. I found out how the clipBounds were changing for each paint call, so when I set the clip at the start of the paintComponent() method, the images show up immediately. (BTW, It looks GREAT!).

With a twist: after an image is displayed, each click of a button does something to the image. I haven't figured out what exactly, though. It almost looks like its repainting the same image over the old images.

So I have to figure out how to keep the old images but draw new ones when appropriate and only draw/add the new ones onto the glassPanel.

UPDATE: Calling repaint() immediately after each button is clicked helps a little. But it still causes the image to flicker somewhat, as if adding another layer, as the button is pressed, and then it returns to normal when the user lets go.