不移动鼠标光标时Java动画会断断续续

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

该程序在某些系统上显示出奇怪的行为。 在我的电脑上使用nVidia卡,它运行顺畅。 在我的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); } } 

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

  Toolkit.getDefaultToolkit().sync(); 

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

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

附录:此外,您在每次调用paintComponent()都会费力地重新渲染整个图像。 相反,使用TextLayout渲染一次 ,在这里看到,并且每次只draw()新的可见部分。

图片

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

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

另请参阅特别是在AWT和Swing中执行自定义绘画和绘画

问题解决了!

回答我自己的问题:在意识到任何连续输入(鼠标或键盘)使动画顺利运行后,我记得输入可以由程序本身使用类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,但完美的工作。