.setVisible(true) immediate repaint

2019-02-22 09:48发布

问题:

In a short method, I hide a JFrame using setVisible(false). Then I take a screenshot and restore the JFrame with setVisible(true).

After made visible again, the window is supposed to show a different picture than before (lets say a part of that screenshot taken).

The problem is that after setVisible(true) is called, the window is flashed with the old content for a split second before paintComponent is called and the updated state is drawn.

I could probably workaround this in an ugly way, but I wanted to know if there are better solutions.

Thanks in advance for any help

edit: While preparing an example, I noticed that the effect was hardly ever observable when not using transparency like I do in my program. Should probably have mentioned that. Here is what I came up with:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;

import javax.swing.JFrame;
import javax.swing.JPanel;

import com.sun.awt.AWTUtilities;
public class Test {

    static boolean flag = false;
    static Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize();

    public static void main(String[] args) throws InterruptedException {
        JFrame frame = new JFrame();
        frame.setUndecorated(true);
        AWTUtilities.setWindowOpaque(frame, false);  //draw on a transparent window
        frame.setSize(scrSize.width, scrSize.height);
        frame.setContentPane(new JPanel() {
            protected void paintComponent(Graphics g) 
            {
                if (Test.flag) {
                    g.setColor(Color.RED);
                    g.drawRect(50, 50, scrSize.width - 100, scrSize.height - 100);
                }
                else {
                    g.setColor(Color.GREEN);
                    g.fillOval(50, 50, scrSize.width - 100, scrSize.height - 100);
                }
            }
        });
        frame.setVisible(true); //green oval shown
        Thread.sleep(1000);
        frame.setVisible(false);
        flag = true; // next draw produces red rectangle
        Thread.sleep(1000);
        frame.setVisible(true); // before the next draw, 
                                // you can see a flash of the green oval
    }

}

回答1:

I realise that this answer is being suggested a year after the question was asked, but I had a similar problem and of course researched a bit before I tried to solve it. For anyone who comes across this question, try disposing your window / frame before you pack and make it visible.



回答2:

This is because each AWT window has an off-screen image that is kept in sync with the on-screen image. When a window is displayed, its contents are painted directly from the off-screen image. This happens on the toolkit thread, not on the event dispatch thread.

Only after the window is shown, the frame is repainted on the event dispatch thread.

Before Java 1.6, there was no per-window double buffering in the AWT, so you would have seen a gray background which was the infamous "gray rectangle" problem.

The only workaround I'm aware of is to create a new frame each time. You can reuse the content panel of the old frame, so the overhead is not so high.