20

我有一个非常简单的动画,一个大字体的文本连续(逐个像素)向左移动。文本首先转换为图像,然后启动计时器任务,该任务重复(每 10-20 毫秒)将图像的 x 坐标减 1,并执行 repaint()。

该程序在某些系统上显示出奇怪的行为。在我的带有 nVidia 卡的 PC 上,它运行流畅。在我的 Vaio 笔记本、BeagleBoneBlack 和朋友的 Mac 上,它严重卡顿。它似乎暂停了一会儿,然后向左跳约 10 个像素,再次暂停,依此类推。

让我感到困惑的是,在这些系统上,动画只有在您不触摸鼠标时才会卡顿。只要您在窗口内移动鼠标光标,无论多么缓慢,或拖动窗口本身,动画都运行得非常流畅!

有人可以解释一下吗?这是程序:

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.*;

class Textimg extends JComponent
{
    String      str;
    Font        font;
    int         x = 0;
    final int   ytext = 136;
    Image       img;

    public Textimg(String s)
    {
        str = s;
        font = new Font("Noserif", Font.PLAIN, 96);
        setLayout(null);
    }

    protected void paintComponent(Graphics g)
    {
        if (img == null)
        {
            img = createImage(4800, 272);
            Graphics gr = img.getGraphics();

            gr.setFont(font);
            gr.setColor(Color.BLACK);
            gr.fillRect(0, 0, 4800, 272);
            gr.setColor(new Color(135, 175, 0));
            gr.drawString(str, 0, ytext);
            gr.dispose();
        }

        g.drawImage(img, x, 0, this);
    }

    public void addX(int dif)
    {
        if (isVisible())
        {
            x = x + dif;

            Graphics g = getGraphics();

            if (g != null) paintComponent(g);
        }
    }
} 


public class Banner extends JFrame 
{ 
    StringBuffer    buf;
    int             sleeptime = 10;

    Banner(String path) throws IOException 
    { 
        setSize(new Dimension(480, 272));
        setTitle("Java Test");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(null);

        BufferedReader reader = new BufferedReader(
                new InputStreamReader(new FileInputStream(path), "UTF-8"));

        buf = new StringBuffer();

        while (true) 
        {
           String line = reader.readLine();

           if (line == null) break;
           buf.append(line);
        }

        final Textimg textimg = new Textimg(buf.toString());

        add(textimg);
        textimg.setBounds(0, 0, 480, 272);

        final javax.swing.Timer timer = new javax.swing.Timer(200, new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                textimg.addX(-1);
            }
        });

        timer.setDelay(sleeptime);
        timer.start();
    }

    //----------------------------------------------------------------------

    public static void main(String[] args) throws Exception
    {
        new Banner(args[0]).setVisible(true);
    }
}
4

4 回答 4

41

绘制完成后尝试调用此方法:

 Toolkit.getDefaultToolkit().sync();

这会刷新某些系统(如 Linux)使用的图形缓冲区。请参阅 Javadoc:http://docs.oracle.com/javase/7/docs/api/java/awt/Toolkit.html#sync()

于 2014-10-15T17:09:05.213 回答
3
  • 不要,永远,使用getGraphics,你不应该称paintComponent自己,这不是在 Swing 中完成自定义绘画的方式。相反,更新状态并调用repaint.
  • 不要依赖幻数,使用你手头的信息(getWidthgetHeight
  • Swing 组件是双倍缓冲的,因此您不太可能需要创建自己的缓冲策略。这个简单的动作可能会减慢你的绘画速度
  • 你必须打电话super.paintComponent。这对 来说更为重要JComponent,因为它不是不透明的,如果不这样做可能会导致一些令人讨厌的油漆伪影。
  • 您应该覆盖JComponent#getPreferredSize,以便它可以有效地与布局管理器一起使用。
  • 您可能会发现较短的延迟会产生更好的错觉,例如 40 毫秒(大约 25fps)

看看Swing 动画运行非常慢,通过一些对象管理和优化,能够从 500 个动画对象增加到 4500 个。

还要看看在 AWT 和 Swing执行自定义绘画和绘画

于 2013-10-20T19:37:58.790 回答
2

分析显示您正在使javax.swing.Timer. 一种缓解策略是使用更长的周期和/或更大的增量/减量,如此处所示

附录:此外,您在每次调用paintComponent(). 相反,使用, seen here渲染一次,并且每次只渲染新可见的部分。TextLayoutdraw()

图片

于 2013-10-20T17:06:28.767 回答
0

问题解决了!

回答我自己的问题:在意识到任何连续输入(鼠标或键盘)都会使动画顺利运行后,我记得输入可以由程序本身生成,使用类的对象java.awt.Robot。这导致了一个简单的解决方法:创建一个机器人并让它在每个动画循环中按下一个键或鼠标移动。

final Robot robot = new Robot();
javax.swing.Timer timer = new javax.swing.Timer(initialDelay, new ActionListener()
{
    public void actionPerformed(ActionEvent e)
    {
        // update your image...

        robot.keyPress(62);
     }
});

这是一个kludge,但效果很好。

于 2013-10-22T10:31:44.243 回答