【问题标题】:How can I avoid flicker when resizing a Swing window?调整 Swing 窗口大小时如何避免闪烁?
【发布时间】:2012-11-17 01:54:44
【问题描述】:

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

我将问题简化为测试:

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

每次单击按钮时,窗口都会改变大小。在相当长的一段时间内,对话框底部变黑。通过录制我的屏幕,我可以得到一个截图来展示它:

我怎样才能避免这种情况?

进一步调查:

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 的子类可以避免这个问题。

【问题讨论】:

  • 嗯...有趣,虽然我无法重现它(vista,jdk7)
  • +1 到 kleopatra,我使用 Windows 7 64 位 JDK 7u9 并且没有闪烁。 @Trejkaz 你为什么还要使用Dialog?应该使用JDialog。也不要调用setSize 而不是覆盖getPreferredSize() 并使其返回heightwidth 的尺寸(不要忘记设置高度和宽度)然后只需在JDialog 上调用revalidate()pack()在调用具有适当值的设置器之后。以here 为例
  • 在未经用户同意的情况下重新调整窗口大小不是很好的可用性。
  • @DavidKroukamp JDialog 也出现了这个问题,这里使用 Dialog 只是为了演示使问题发生所需的最少 JDialog 量。我从 JDialog 的完整副本开始并删除了代码,直到闪烁停止。实际应用程序使用 pack() 并没有什么区别。此示例已简化为 setSize() 以减少演示问题所需的代码量。
  • @kazanaki 但是很多应用程序都这样做,甚至是真正可用的应用程序。几乎 Mac OS X 应用程序上的每个首选项对话框都会这样做。在我的情况下,它是一个查找对话框,在移动到另一个组件时可能会出现其他设置,所以我必须对其他设置做一些事情,而不是用空白空间填充对话框,即使你不是将移动到其他组件...

标签: java swing dialog


【解决方案1】:

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

【讨论】:

  • 哇,你说得对。完全没有闪烁。我会看看这两个,并尝试弄清楚 JDialog 是什么导致了这种情况发生......也许我可以绕过它。
  • 将所有 Swing 内容放入基于 AWT 的顶级容器中应该没有什么问题。从 1.7 开始,应该完全没有问题。详情请见Mixing Heavyweight and Lightweight Components
  • 是的,我最大的恐惧是不得不重新实现一些 JDialog 正在做而 Dialog 没有做的事情。
  • 废话,废话,关于 Dialog 的事情。它确实闪烁,但它是另一种闪烁。而不是底部变黑然后绘画,整个对话框变成浅灰色然后绘画。两者哪个更差可能取决于个人喜好,但 JDialog 方式需要的代码更少,所以我想我现在就这样做。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-29
    • 2016-03-19
    • 2015-08-29
    • 2022-10-13
    • 2014-01-24
    • 1970-01-01
    • 1970-01-01
    • 2018-07-27
    相关资源
    最近更新 更多