3

I have a dialog where additional controls cause the dialog to resize when they appear. One day I will probably find a way to animate that, but for now I'm content with it just resizing. Problem is, it flickers.

I reduced the problem to a test:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 * Shows flickering when resizing a dialog.
 */
public class FlickerTest extends FakeJDialog
{
    public FlickerTest()
    {
        super((Window) null, "Flicker Test");

        JButton button = new JButton("Bigger!");
        button.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent event)
            {
                Window window = SwingUtilities.getWindowAncestor((Component) event.getSource());
                window.setSize(window.getWidth(), window.getHeight() + 20);
            }
        });

        JPanel contentPane = new JPanel(new BorderLayout());
        contentPane.setOpaque(true);
        contentPane.add(button, BorderLayout.PAGE_START);

        JRootPane rootPane = new JRootPane();
        rootPane.setContentPane(contentPane);

        add(rootPane);

        setResizable(false);
        pack();
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) throws Exception
    {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                new FlickerTest().setVisible(true);
            }
        });
    }
}

Each time I click the button, the window changes size. For a noticeable amount of time, the bottom of the dialog goes black. By recording my screen, I was able to get a screenshot demonstrating it:

enter image description here

How can I avoid this?

Further investigation:

The following subclass of Dialog exhibits the same flickering as JDialog:

import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Window;

import javax.swing.JLayeredPane;
import javax.swing.JRootPane;
import javax.swing.RootPaneContainer;

/**
 * Minimal subclass of Dialog required to cause the flickering.
 * If you comment out "implements RootPaneContainer", the flickering goes away.
 */
public class FakeJDialog extends Dialog implements RootPaneContainer
{
    public FakeJDialog(Window owner, String title)
    {
        super(owner, title, Dialog.ModalityType.MODELESS);
    }

    public JRootPane getRootPane()
    {
        throw new UnsupportedOperationException();
    }

    public Container getContentPane()
    {
        throw new UnsupportedOperationException();
    }

    public void setContentPane(Container contentPane)
    {
        throw new UnsupportedOperationException();
    }

    public JLayeredPane getLayeredPane()
    {
        throw new UnsupportedOperationException();
    }

    public void setLayeredPane(JLayeredPane layeredPane)
    {
        throw new UnsupportedOperationException();
    }

    public Component getGlassPane()
    {
        throw new UnsupportedOperationException();
    }

    public void setGlassPane(Component glassPane)
    {
        throw new UnsupportedOperationException();
    }
}

I find this kind of interesting, because merely commenting out the implements RootPaneContainer is somehow enough to completely change the behaviour. Something in Swing or AWT is obviously looking for this interface and treating those components specially. So this suggests that no subclass of JDialog would avoid the issue.

David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
Hakanai
  • 12,010
  • 10
  • 62
  • 132
  • 1
    hmm ... interesting, though I can't reproduce it (vista, jdk7) – kleopatra Nov 29 '12 at 09:59
  • +1 to kleopatra, I use Windows 7 64 bit JDK 7u9 and no flickering. @Trejkaz why are you using `Dialog` anyway? should use `JDialog`. Also do not call `setSize` rather override `getPreferredSize()` and make it return dimensions of `height` and `width` (dont forget to have setters for height and width) then simply call `revalidate()` and `pack()` on `JDialog` after calling setters with appropriate value. see [here](http://stackoverflow.com/questions/13549976/how-to-change-the-dimension-of-a-component-in-a-jframe/13551229#13551229) for example – David Kroukamp Nov 29 '12 at 10:05
  • Re-sizing a window without the user's consent is not great usability. – kazanaki Nov 29 '12 at 14:59
  • @DavidKroukamp JDialog exhibits the issue too, the usage of Dialog here is solely to demonstrate the minimal amount of JDialog required to make the problem occur. I started from a complete copy of JDialog and deleted code until the flickering stopped. The actual application uses pack() and that makes no difference. This example was simplified to setSize() to reduce the amount of code required to demonstrate the issue. – Hakanai Nov 29 '12 at 22:12
  • @kazanaki But so many apps do it, even really usable ones. Practically every preferences dialog on a Mac OS X app does it. In my case it's a Find dialog where additional settings can appear on moving to another component, so I have to do *something* about the additional settings, other than filling the dialog with empty space even when you're not going to move to that other component... – Hakanai Nov 29 '12 at 22:14
  • @kleopatra yeah, that's exactly why I figured I would attach a screenshot. Some other person in our company couldn't reproduce it either. – Hakanai Nov 29 '12 at 22:15
  • yeah, suspected that it is highly dependent on the conrete context. What is yours? The frame border looks solid, on win that would be metro or 6/7 with a theme having not-transparent borders? – kleopatra Nov 30 '12 at 10:47
  • Metro, yeah. Windows 8 with Java 7u7. It could be a graphics driver thing. :( About the only thing I can say about that is that native apps don't seem to flicker when doing similar things. – Hakanai Dec 04 '12 at 00:47

2 Answers2

2

I don't believe there is a way around this with JDialog. However, java.awt.Dialog doesn't have this issue.

mikeslattery
  • 4,039
  • 1
  • 19
  • 14
  • Wow, you're right about that. No flicker at all. I'll have a look at the two and try to figure out what it is about JDialog which makes this happen... maybe I can step around it. – Hakanai Nov 29 '12 at 04:12
  • There should have been few problems putting all Swing content into an AWT based top-level container. As of 1.7, there should be no problems at all. See [Mixing Heavyweight and Lightweight Components](http://www.oracle.com/technetwork/articles/java/mixing-components-433992.html) for details. – Andrew Thompson Nov 29 '12 at 04:23
  • Yeah, my biggest fear is having to reimplement some things which JDialog was doing that Dialog isn't. – Hakanai Nov 29 '12 at 05:29
  • Crap, scratch the thing about Dialog. It does flicker, but it's a different kind of flickering. Instead of the bottom going black and then painting, the entire dialog goes light grey and then paints. Which of the two is worse is probably up to personal preference, but the JDialog way requires less code, so I guess I'm going with that for now. – Hakanai Nov 29 '12 at 23:27
0

Try this, i have made an example which removes flickering almost completely

in addition u will get well-marked resize corner

enter image description here

/*
 * resizing swing trick in Win7+Aero demo
 * @author: s1w_
*/
import java.awt.event.*;
import java.awt.*;
import javax.swing.event.*;
import javax.swing.*;

class ResizeHookDemo extends JDialog {
  private final static int width = 580, height = 350;
  private final JFileChooser fc;
  private java.awt.geom.GeneralPath gp;

  public ResizeHookDemo() {
    super((JDialog)null, "Choose File", true);

    fc = new JFileChooser() {

     @Override
     public void paint(Graphics g) {
       super.paint(g);
       int w = getWidth();
       int h = getHeight();
       g.setColor(new Color(150, 150, 150, 200));
       g.drawLine(w-7, h, w, h-7);
       g.drawLine(w-11, h, w, h-11);
       g.drawLine(w-15, h, w, h-15);

       gp = new java.awt.geom.GeneralPath();
       gp.moveTo(w-17, h);
       gp.lineTo(w, h-17);
       gp.lineTo(w, h);
       gp.closePath();
     }

    };
    fc.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("CancelSelection")) {
          setVisible(false);
          // action...
        }
        else if (e.getActionCommand().equals("ApproveSelection")) {
          setVisible(false);
          // action...
        }
      }
    });

    MouseInputListener resizeHook = new MouseInputAdapter() {
      private Point startPos = null;

      public void mousePressed(MouseEvent e) {
        if (gp.contains(e.getPoint()))
          startPos = new Point(getWidth()-e.getX(), getHeight()-e.getY());
      }

      public void mouseReleased(MouseEvent mouseEvent) {
        startPos = null;
      }

      public void mouseMoved(MouseEvent e) {
        if (gp.contains(e.getPoint()))
          setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
        else
          setCursor(Cursor.getDefaultCursor());
      }

      public void mouseDragged(MouseEvent e) {
        if (startPos != null) {

          int dx = e.getX() + startPos.x;
          int dy = e.getY() + startPos.y;

          setSize(dx, dy);
          repaint();
        }
      }
    };

    fc.addMouseMotionListener(resizeHook);
    fc.addMouseListener(resizeHook);
    fc.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 20));
    add(fc);

    setResizable(false);

    setMinimumSize(new Dimension(width, height));
    setDefaultCloseOperation(HIDE_ON_CLOSE);
    setLocationRelativeTo(null);
  }

  public static void main(String args[]) {
    System.out.println("Starting demo...");
    SwingUtilities.invokeLater(new Runnable() {

      @Override
      public void run() {
        new ResizeHookDemo().setVisible(true);
      }
    });
  }
}
s1w_
  • 133
  • 8