尽管使用了paintComponent和动画线程,但使用Java / Swing的动画是跳跃的

时间:2015-02-24 20:51:40

标签: java swing animation

我有一个简单的Java / Swing应用程序,试图通过从左到右移动框来设置框的动画:

public class TestFrame extends JFrame {
    protected long startTime = new Date().getTime();

    public class MainPanel extends JPanel {
        @Override
        protected void paintComponent(Graphics g) {
            // calculate elapsed time
            long currentTime = new Date().getTime();
            long timeDiff = currentTime - TestFrame.this.startTime;

            // animation time dependent
            g.fillRect((int) (timeDiff / 100), 10, 10, 10);
        }
    }

    public class MainLoop implements Runnable {
        @Override
        public void run() {
            while (true) {
                // trigger repaint
                TestFrame.this.repaint();
            }
        }
    }

    public static void main(String[] args) {
        new TestFrame();
    }

    public TestFrame() {
        // init window
        this.setTitle("Test");
        this.setSize(500, 500);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(new MainPanel());
        this.setVisible(true);

        // start render loop
        Thread loop = new Thread(new MainLoop());
        loop.start();
    }
}

问题是动画不干净,框跳转(有时)几个像素。我已经做过一些研究,根据它们,如果使用paintComponent(而不是paint)并进行基于时间的动画(不基于帧),它应该可以正常工作。我做了两个,但动画仍然不干净。

有人能给我一个暗示出了什么问题吗?

3 个答案:

答案 0 :(得分:2)

你应该给你的while-true-loop稍微休息一下。你有点燃烧你的CPU!你正在制作大量的油漆事件;在线程调度程序决定的某个时间,调度程序切换到事件调度线程,据我所知,可能会将您的绘制事件的trillon折叠成一个并最终执行paintComponent。

在以下示例中,线程休眠20ms,最大帧速率为50fps。那应该够了。

while (true) {
    // trigger repaint
    TestFrame.this.repaint();

    try {
        Thread.sleep(20);
    } catch(InterruptedException exc() {
    }
}

答案 1 :(得分:0)

你在我的机器上的绘画相当流畅,但它在那个循环中燃烧了很多性能,但在较慢的机器上,我可以想象如果你的应用程序忙于执行while或处理paint事件而动画是跳跃的渲染。

根据每次渲染经过多长时间更新位置可能更好,我不确定通过Date个对象比较动画时间的准确程度,因此使用{比较小的时差{1}}可能会更好。例如:

System.nanoTime()

答案 2 :(得分:0)

我对您的代码进行了一些更改。

  1. 我调用了SwingUtilities invokeLater方法来创建和使用Event Dispatch thread上的Swing组件。

  2. 我调用了系统currentTimeinMillis方法来获取当前时间。

  3. 我没有设置JFrame大小,而是设置了JPanel的大小并打包了JFrame。我缩小了JPanel的大小以加快重新绘制。

  4. 我在while(true)循环中添加了一个延迟,正如fjf2002在他的回答中所建议的那样。

  5. 以下是经过修改和格式化的代码:

    package com.ggl.testing;
    
    import java.awt.Dimension;
    import java.awt.Graphics;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class TestFrame extends JFrame {
    
        private static final long serialVersionUID = 272649179566160531L;
    
        protected long startTime = System.currentTimeMillis();
    
        public class MainPanel extends JPanel {
    
            private static final long serialVersionUID = 5312139184947337026L;
    
            public MainPanel() {
                this.setPreferredSize(new Dimension(500, 30));
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                // calculate elapsed time
                long currentTime = System.currentTimeMillis();
                long timeDiff = currentTime - TestFrame.this.startTime;
    
                // animation time dependent
                g.fillRect((int) (timeDiff / 100), 10, 10, 10);
            }
        }
    
        public class MainLoop implements Runnable {
            @Override
            public void run() {
                while (true) {
                    // trigger repaint
                    TestFrame.this.repaint();
    
                    try {
                        Thread.sleep(20L);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new TestFrame();
                }
            });
        }
    
        public TestFrame() {
            // init window
            this.setTitle("Test");
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.add(new MainPanel());
            this.pack();
            this.setVisible(true);
    
            // start render loop
            Thread loop = new Thread(new MainLoop());
            loop.start();
        }
    }