Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.setVisible(true) immediate repaint

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
    }

}
like image 671
Zymox Avatar asked Oct 12 '11 01:10

Zymox


1 Answers

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.

like image 92
Ingo Kegel Avatar answered Nov 04 '22 22:11

Ingo Kegel