调用Drawable.draw()时防止“闪烁”

时间:2011-02-13 17:10:54

标签: android android-emulator surfaceview

我有一个小实验应用程序(本质上是Android SDK中LunarLander演示的非常缩减版本),只有一个SurfaceView。我有一个Drawable“精灵”,我定期在不同位置绘制到SurfaceView的{​​{1}}对象,而不会尝试删除上一张图像。因此:

Canvas

在模拟器中运行,我看到的是,在发生一些更新之后,图像的几个旧“副本”开始闪烁,即出现和消失。我最初假设也许我误解了private class MyThread extends Thread { SurfaceHolder holder; // Initialised in ctor (acquired via getHolder()) Drawable sprite; // Initialised in ctor Rect bounds; // Initialised in ctor ... @Override public void run() { while (true) { Canvas c = holder.lockCanvas(); synchronized (bounds) { sprite.setBounds(bounds); } sprite.draw(c); holder.unlockCanvasAndPost(c); } } /** * Periodically called from activity thread */ public void updatePos(int dx, int dy) { synchronized (bounds) { bounds.offset(dx, dy); } } } 的语义,并且它以某种方式保持“层次”,并且我正在将它颠倒过来。然而,我后来发现,如果我尝试以大约每200毫秒的速度更新,我只会得到这种效果。所以我的下一个最好的理论是,这可能是模拟器无法跟上并撕裂显示器的神器。 (我还没有可以测试的物理设备。)

这些理论中的任何一个都是正确的吗?

注意:我实际上并不想在实践中这样做(即绘制数百个相同内容的重叠副本)。但是,我想了解为什么会发生这种情况。

环境:

  • Windows 7上的Eclipse 3.6.1(Helios)
  • JDK 6
  • Android SDK工具r9
  • App正在定位Android 2.3.1

切线问题:

我的Canvas方法本质上是LunarLander示例如何工作的精简版本(删除了所有多余的逻辑)。我不太明白为什么这不会让CPU饱和,因为似乎没有什么可以阻止它以完全的毛皮运行。任何人都可以澄清这个吗?

2 个答案:

答案 0 :(得分:12)

好吧,我以与你类似的方式屠杀了Lunar Lander,看到闪烁的我可以告诉你,你所看到的是双缓冲机制的一个简单的人工制品,每个Surface都有

当您在附加到Canvas的{​​{1}}上绘制任何内容时,您将绘制到“后退”缓冲区(不可见的缓冲区)。当你Surface你正在交换缓冲区时......你突然变得可见,因为“后退”缓冲区变成“前面”,反之亦然。所以你的下一帧绘图是在旧的“前”缓冲区完成的......

重点是你总是在备用帧上绘制单独的缓冲区。我想在图形架构中存在一个隐含的假设,即总是将写入每个像素。

理解了这一点后,我认为真正的问题是它为什么不在硬件上闪烁?在经历了多年的图形驱动程序研究之后,我可以猜测原因,但却犹豫不决。希望上述内容足以满足您对此渲染工艺的好奇心。 : - )

答案 1 :(得分:1)

您需要清除精灵的先前位置以及新位置。这就是View系统自动执行的操作。但是,如果直接使用Surface并且不重绘每个像素(使用不透明颜色或使用SRC混合模式),则必须自己清除缓冲区的内容。请注意,您可以将脏矩形传递给lockCanvas(),它将为您执行上一个脏矩形和您传递的矩形的并集(这是UI工具包使用的机制。)它还将设置剪辑rect画布是这两个矩形的结合。

至于你的第二个问题,unlockAndPost()会做一个vsync,所以你永远不会以超过60fps的速度进行绘制(我见过的大多数设备的显示刷新率设置在55Hz左右。)