5

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
    }

}
WhiteKnight
  • 4,938
  • 5
  • 37
  • 41
Zymox
  • 109
  • 4
  • 8

2 Answers2

4

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.

Ingo Kegel
  • 46,523
  • 10
  • 71
  • 102
  • 1
    sounds reasonable somehow - just: I cant reproduce the problem at all. So before going with a newly created frame, I would suggest to track down the real reason :-) – kleopatra Oct 12 '11 at 08:54
  • It only happens for complex frames and only if you change something in the frame while it is invisible. The reason is explained in my answer. AFAIK, there is nothing you can do about that short of creating a new frame. – Ingo Kegel Oct 12 '11 at 08:59
  • as I said: the reasoning sounds good, and I certainly change the content before showing again (how else could I try to reproduce ). So the difference might be the _complex_ part, how complex would that be? – kleopatra Oct 12 '11 at 09:06
  • The frame is shown and painted from the off-screen image and only then the root pane is validated and repainted. Depending on how log that takes, you still see the old content for a while. – Ingo Kegel Oct 12 '11 at 09:10
  • yeah, I understand the reasoning ... still unconvinced until I see it (stubborn me :) will try on my netbook eventually. Thanks – kleopatra Oct 12 '11 at 09:12
  • Thanks for your answers. I have included an example above, should be copy, paste, run. Preparing this, I noticed that the effect is hardly ever (if ever) observable when not using transparency, which I do in my program. I believe to have seen some very short flashes without transparency, but that might just as well have been my crazy brain that had to stare for flashing green ovals for minutes =) – Zymox Oct 12 '11 at 17:45
3

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.

rtheunissen
  • 7,347
  • 5
  • 34
  • 65
  • THIS should be the accepted answer. Creating a new frame for every time you show the dialog is not acceptable. – Tustin2121 Apr 26 '16 at 20:19
  • Are you sure disposing the window is ok to do? I thought I read in Oracle docs that this frees native resources and is only to be done when you are finished with the frame/window. – Jason Dec 01 '18 at 17:57