簡體   English   中英

在 swing 中調整組件大小時如何防止閃爍?

[英]How to prevent flickering when resizing a component in 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()
    {
        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();
    }
}

只是為了注意:在 Win 7 中使用 Aero 調整大小時閃爍是已知問題: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6898838 http://bugs.sun.com/bugdatabase/view ?bug_id=6873928

不要在 Timer 中使用 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();
    }
}

順便說一句 - 我們很多人沒有 1500 像素寬的顯示器(+ 以說明 UI 從屏幕中間開始的事實)像素寬。

情侶建議:

  • 使用 30 毫秒而不是 1 毫秒的延遲。 30 毫秒為您提供每秒 30 幀的流暢幀數,這已經足夠了。
  • 使用setBounds而不是setSize 不確定這是否會有所不同,但可以更好地控制坐標。
  • 不要在 Timer 中調用sleep() ,而是在Timer上設置初始延遲
  • 調用setPreferredSize(0, 0)

public static 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.setBackground(Color.gray);

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

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


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

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

            }
        });

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

}

這個技巧提高了 Win7+Aero 中的重繪率:將 resizable 設置為 null,並提供自己的 resize hook。 它不是完美的,但仍然好多了..查看我的示例:

在此處輸入圖像描述

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

對我來說,將 noerasebackround 變為真正的工作:

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

對此感到抱歉-我沒有多想就匆匆回答了。 是的,不應在 Swing 中使用更新。

為了彌補,我發現這篇博文是由 OS X Swing 的人寫的,他在第 2 段中寫道,“程序化調整大小目前導致 Mac 上 Java 閃爍。”

http://explodingpixels.wordpress.com/2008/09/28/the-heads-up-display-hud/

由於您的代碼在我運行它時不會導致閃爍(在工作中的舊 XP 機器上),看來上述情況仍然正確。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM