Java 如何防止在swing中调整组件大小时闪烁?

Java 如何防止在swing中调整组件大小时闪烁?,java,swing,Java,Swing,如果我运行下面的示例,我会在JSplitPane的右侧看到闪烁。有没有办法避免这种情况 import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; public class FlickerTest { int width = 1; private void create() { fina

如果我运行下面的示例,我会在JSplitPane的右侧看到闪烁。有没有办法避免这种情况

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class FlickerTest
{
    int width = 1;

    private void create()
    {
        final JFrame f = new JFrame("JSplitPane");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel p1 = new JPanel();
        p1.setPreferredSize(new Dimension(100, 300));

        JPanel p2 = new JPanel();
        p2.setPreferredSize(new Dimension(0,0));
        p2.setBackground(Color.gray);

        JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, p1, p2);
        jsp.setSize(new Dimension(400, 800));

        Timer timer = new Timer(1, new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                width++;

                if (width == 2)
                {
                    try
                    {
                        Thread.sleep(1500);
                    }
                    catch (Exception ex)
                    {
                    }
                }

                int frameWidth = f.getWidth() + width;
                Dimension d    = new Dimension(frameWidth, f.getHeight());
                f.setSize(d);

                if (width > 20)
                {
                    Timer t = (Timer) e.getSource();
                    t.stop();
                }

            }
        });

        f.add(jsp);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        timer.start();
    }

    public static void main(String[] args) throws Exception
    {
        new FlickerTest().create();
    }
}

不要在计时器中使用Thread.sleep()。您正在阻止EDT响应事件和进行绘制。

这对您有用吗

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class FlickerTest
{
    int width = 1;

    private void create()
    {
        final JFrame f = new JFrame("JSplitPane");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel p1 = new JPanel();
        p1.setPreferredSize(new Dimension(100, 300));

        JPanel p2 = new JPanel();
        p2.setPreferredSize(new Dimension(0,0));
        p2.setBackground(Color.gray);

        JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, p1, p2);
        jsp.setSize(new Dimension(400, 800));

        Timer timer = new Timer(1, new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                width++;

                int frameWidth = f.getWidth() + width;
                if (width>1502) {
                    frameWidth = f.getWidth() + width - 1500;
                }
                Dimension d    = new Dimension(frameWidth, f.getHeight());
                f.setSize(d);
                if (width > 1520)
                {
                    Timer t = (Timer) e.getSource();
                    t.stop();
                }

            }
        });

        f.add(jsp);
        f.pack();
        //f.setLocationRelativeTo(null);
        f.setVisible(true);
        timer.start();
    }

    public static void main(String[] args) throws Exception
    {
        new FlickerTest().create();
    }
}

< P>对不起,我没有考虑太多地冲掉了我的答案。是,不应在Swing中使用更新

为了弥补这一点,我找到了一个OSX Swing家伙的博客文章,他在第2段写道,“编程调整当前会导致Mac上Java的闪烁。”

由于您的代码在我运行它时(在这里工作的旧XP机器上)不会引起闪烁,因此上述情况似乎仍然正确。

几点建议:


  • 使用30毫秒而不是1毫秒的延迟。30毫秒每秒可为您提供30帧的平滑效果,这已经足够了
  • 使用
    setBounds
    代替
    setSize
    。不确定这是否会产生影响,但会提供对坐标的更多控制
  • 不要在
    计时器中调用
    sleep()
    ,而是在计时器上设置初始延迟
  • 调用
    setPreferredSize(0,0)


请注意:在Win 7中,Aero的已知问题是在调整大小时闪烁:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6873928

这个技巧提高了Win7+Aero中的重绘率:将resizeable设置为null,并提供自己的resize挂钩。它不是完美的,但还是好得多。。请看我的例子:

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


对于我来说,将NoeraseBack转变为true起到了作用:

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

sleep调用与闪烁无关,因为它只在事件的第一次触发时被调用,而不是在随后的事件中被调用。也许,也许不是,重点是你不应该在EDT上使用ad.sleep()。在我的机器上,这个代码没有闪烁。顺便说一句,我在Mac OSX上运行这个。嗯,也许这是Mac的问题。当我有访问权限时,我必须在windows框上重试。谢谢。是的,我的是窗户。但是你应该真正尊重Swing线程模型,尽管我不确定这是否是给你带来麻烦的原因。应该补充的是,我也从未见过运行Windows7和Java1.6.0-b105的flicker。代码的两个版本都没有闪烁,但原始版本“跳”到非常宽,而我发布的版本逐像素增加。不,这没有任何区别。@John Thorhauer工作正常,没有冻结调整大小,因为您的示例Andrew Thompson+1I覆盖了更新()调用JPanel和SplitPane,但仍然存在相同的问题。顺便说一句,我在MacOSX上运行这个。不确定这是否有区别。@john,@hover,我原来的答案是错的,所以我改变了它。“30ms给你一个平滑的每秒30帧,…”试试“33&1/3fps”。+1我开始在大约30毫秒或33毫秒时看到平滑的结果⅓ Hz.:-)计时器从1变为30似乎起到了相当大的作用。
System.setProperty("sun.awt.noerasebackground", "true");