调整Swing窗口大小时如何避免闪烁?

时间:2012-11-29 03:12:23

标签: java swing dialog

我有一个对话框,其他附加控件会在对话框出现时调整大小。有一天,我可能会找到一种方法来制作动画,但是现在我很满意它只是调整大小。问题是,它闪烁着。

我将问题简化为测试:

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);
            }
        });
    }
}

每次单击按钮,窗口都会改变大小。在明显的时间内,对话框的底部变黑。通过录制我的屏幕,我能够获得一个屏幕截图来展示它:

enter image description here

我该如何避免这种情况?

进一步调查:

以下Dialog子类表现出与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();
    }
}

我觉得这很有意思,因为仅仅评论implements RootPaneContainer就足以完全改变行为。 Swing或AWT中的东西显然正在寻找这个界面并特别处理这些组件。所以这表明没有JDialog的子类可以避免这个问题。

2 个答案:

答案 0 :(得分:2)

我不相信JDialog可以解决这个问题。但是,java.awt.Dialog没有此问题。

答案 1 :(得分:0)

试试这个,我做了一个几乎完全消除闪烁的例子

另外你会得到标记清晰的角落

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);
      }
    });
  }
}