13

As discussed here, when resizing a Swing application in Vista (and Windows 7, which is what I'm using) you get a black background in the right/bottom corner while Swing's repaint catches up to the changes.

Playing with other applications (Windows Explorer (Native), Firefox (C++?), and Eclipse (Java)) I notice that they all have this same problem - contrary to what the folks in the link above say - but they minimize the problem by having a grey fill color, which is far less visually jarring than the black that appears in Swing.

I'm wondering if there's some way to change this so that Swing behaves like these other applications? I tried setting the background color of the JFrame, but to no avail.

Additional Info Jonas discovered (see their informative answer below) that this is an issue with JFrames, but not AWT Frames - maybe that will help someone figure this out.

dimo414
  • 47,227
  • 18
  • 148
  • 244
  • Can you give a quick code snippet? – bragboy May 06 '10 at 07:41
  • @Bragaadeesh: You can run the code in my answer. – Jonas May 06 '10 at 07:41
  • @Jonas: Yeah got it, its a bit strange, I've been working so long with Swings, but have not noticed it. I think I have one or two alternates, will try to do something on this issue. Nice Question, by the way +1 – bragboy May 06 '10 at 07:48

3 Answers3

3

I have noticed the same problem. This color is gray at IE, in Opera it's black, and in Eclipse it's gray. It seam to be more visible in Swing, because it seam to be little slower at repainting and the color is as you said, black. This problem is more visible if you use the upper left corner to resize.

I coded an example and tried to understand where this black color is defined. A JFrame has many layers, so I set a different background on every layer.

import java.awt.Color;
import javax.swing.JFrame;

public class BFrame {

    public static void main(String[] args) {
        new JFrame() {{
            super.setBackground(Color.CYAN);
            this.getRootPane().setBackground(Color.BLUE);
            this.getLayeredPane().setBackground(Color.RED);
            this.getContentPane().setBackground(Color.YELLOW);
            this.setSize(400,340); 
            this.setVisible(true);
        }};
    }
}

But this example didn't help. And maybe the color is set by a superclass to Frame.

java.lang.Object
  java.awt.Component
      java.awt.Container
          java.awt.Window
              java.awt.Frame

My teory is that, since Swing paints itself, but uses a native Window, then is the native background painted before the resize, and the background of Swing is painted after the resize. But for native applications, the background is painted before the resize.

UPDATE: I tried with a Frame now, and it's not having the same problem. The background seam to be painted before the resize.

import java.awt.Color;
import java.awt.Frame;

public class B2Frame extends Frame {

    public static void main(String[] args) {
        new Frame() {{
            setBackground(Color.YELLOW);
            setSize(400,340);
            setVisible(true);
        }};
    }

}
Jonas
  • 121,568
  • 97
  • 310
  • 388
  • Thanks for the detailed answer, and trying all that stuff. I wonder why the difference, and if there's any way to fix it. I wonder if there's even any way to set the color of the JFrame itself (as opposed to getContentPane(), the only one that seems to do anything). I just tried getting the graphics object of the JFrame and setting its color, but that didn't work either. Any thoughts perhaps on where the default gray background is set? Maybe that has something to do with it. – dimo414 May 06 '10 at 09:27
  • @dimo414: The default gray is set in JFrame: http://fuseyism.com/classpath/doc/javax/swing/JFrame-source.html with `setBackground(UIManager.getDefaults().getColor("control"));` – Jonas May 06 '10 at 10:18
  • @dimo414: You can get rid of this problem if you skip the native titlebar and border. See my question and answer at: http://stackoverflow.com/questions/2780780/how-to-add-support-for-resizing-when-using-an-undecorated-jframe/2781132#2781132 – Jonas May 06 '10 at 13:05
  • True, I could do that, however that seems to me a very round-about solution, and, if you'll forgive me, a far more complicated one than I'm looking for. /Especially/ given that you found Frame does not have this same problem, I still wonder if there isn't some way to do the same in Swing, without resorting to abandoning Windows Aero entirely. – dimo414 May 08 '10 at 09:25
  • I'm fairly sure Frame does have the same problem, just it redraws a bit quicker so it is less noticeable. In the solution I work with, with minor contents the Frame resizes nearly instantly, but once content builds up a bit, the black background is definitely visible when resizing. I think it's really a platform problem in that Java does not interact correctly with Aero to set the colour. – Charles Goodwin Jun 08 '11 at 11:08
2

The frame is responsible for painting its background so you need to make sure you let it do its job.

You demonstrate this by setting:

System.setProperty("sun.awt.noerasebackground", "true");

This will cause the background to always be black on resize expansions. (So don't do it.)

The following worked for me:

  1. (AWT only) Set up double buffering using createBufferStrategy(2) - wrapped in addNotify() otherwise you'll run into exceptions during Frame creation

    (Step 1 is only necessary in AWT as Swing is double buffered by default.)

  2. Always (important) call super() in your Frame.paint() implementation

  3. Set the background colour with setBackground() then the background should always be the same colour when expanding the frame

Sample code:

    class InnerFrame extends Frame {
        public void addNotify() {
            super.addNotify();
            // Buffer
            createBufferStrategy(2);           
            strategy = getBufferStrategy();
        }

        public void paint(Graphics g) {
            super(g);
            //...
        }
        //...
     }
Charles Goodwin
  • 6,402
  • 3
  • 34
  • 63
  • Interesting, thank you. I'll try to look into this next time I'm working on one of my Swing projects. – dimo414 Feb 14 '11 at 02:21
  • I also found that adding a ComponentListener to the primary child of a Frame seems to help a lot with resize issues (added to original answer). – Charles Goodwin Feb 15 '11 at 20:51
  • Updated with solution based on fixing this over the last several months – Charles Goodwin Sep 15 '11 at 15:03
  • I hadn't looked at this question in months, then I looked at it maybe 20 minutes ago, then you posted an edit o_O Thanks! I'll play with it tonight when I'm on a windows machine. – dimo414 Sep 15 '11 at 15:20
  • Sorry for the delay, I tried to implement the code snippets in your answer, and as best I can tell it's not doing anything. Calling super.paint(g) is important, yes, but this issue appears even if you aren't overriding paint() at all. And setBackground() doesn't have an immediate impact either. Try running the classes Jonas posted in his answer, neither extending paint() nor implementing ComponentListener has any effect on the black sub-background. – dimo414 Jan 16 '12 at 09:46
1

I also noticed this. For me this issue was solved with changing layout manager (I've used Free Form Layout before) and it worked pretty well (system color painting).

But eventually I switched back to FFL. Also some well known apps face this problem (f.e. SKYPE), but I actually don't mind it ...

Xorty
  • 18,367
  • 27
  • 104
  • 155