SurfaceView drawText 闪烁
Posted
技术标签:
【中文标题】SurfaceView drawText 闪烁【英文标题】:SurfaceView drawText flickering 【发布时间】:2015-12-28 11:45:52 【问题描述】:我正在尝试使用SurfaceView
制作一个简单的秒表。它显示百分之一秒,因此它会尝试尽可能频繁地更新以保持流畅的动画。它在运行 4.0.3 的平板电脑和运行 4.0.4 的手机上运行良好,但是当我在 4.4.4 上尝试时,似乎有些撕裂,我无法解释。
底部的图像显示了应该发生的事情,并且在大多数情况下它是有效的。但是,每隔几秒钟就会出现一次闪烁。当我设法在正确的时间暂停时,我看到 一些 的数字被移到了右边。他们只会向右移动(上图)。这只发生了几分之一秒,刚好足以引起明显的闪烁。
请注意,数字使用一次计算的值(在onMeasure()
中)定位在画布上,并存储在类字段中。它们在初始化后不会改变。我通过记录onDraw()
内部的每个像素值来测试这一点。
在上图中,转置的是分钟值,但有时是其他数字。它们似乎总是在一组中被调换;分钟、秒或厘秒。
进一步:这只影响drawText()
。我有这个秒表的更复杂版本,有很多路径、圆圈等。问题只发生在文本上。
我查看了关于 SO 和其他地方的几个问题和 cmets。这是我确定的:
我认为这不是缓冲区问题。行为似乎类似于 基于缓冲区的撕裂的描述,但在这种情况下,我不认为 问题可能是旧数据被发布到屏幕上,因为 数字从不在它们显示的位置绘制。
我在评论中读到它可能与
更新在 onDraw()
之间多次调用的值
方法调用。我已尝试限制更新,但问题
持续存在。
我还尝试绘制到临时位图并使用canvas.drawBitmap()
onDraw()
函数,再次无济于事。
基本上,我想知道:
是什么导致了这个问题? 为什么它只在更高版本的 Android 中? 我该怎么办?【问题讨论】:
听起来不像是“闪烁”,而是偶尔在错误的位置绘制文本。当您说您绘制临时位图“无济于事”时,这是否意味着您继续看到在错误位置呈现的分钟数字?它总是分钟数字吗?使用screenrecord
获取录音,然后逐帧浏览。当框架绘制不正确时,计数器是向前前进还是向后跳跃?覆盖onDraw()
在 SurfaceView 上绘图是个坏主意;重命名函数,去掉“@Override”,并确认问题仍然存在。
我确实尝试将绘图放入自定义方法中,这解决了问题,但导致帧率大幅下降,所以我重新使用 onDraw。我会按照你的建议尝试录制。
如果您使用的是onDraw()
,那么当应用程序框架刷新 View UI 时,您将在 View 上绘图(就像自定义 View 一样)而不是 Surface。 View 与 Surface 重叠,因此您在两个重叠的图层上绘图。 Surface 的大小不必与 View 的大小相同,因此使用onMeasure()
是不合适的(参见SurfaceHolder#setFixedSize()
)。您可能应该改用自定义视图:developer.android.com/training/custom-views/index.html
有趣...我最初尝试使用自定义视图,但帧率从来没有高到足以使动画看起来流畅。
按速度递减顺序: (1) 使用 OpenGL 渲染到 SurfaceView Surface; (2) 使用 Canvas 渲染到硬件加速的自定义视图上; (3) 使用 Canvas 渲染到 SurfaceView Surface 上。并非所有 Canvas 操作都可以在所有版本的 Android 上在硬件中完成;见developer.android.com/guide/topics/graphics/hardware-accel.html。
【参考方案1】:
阅读@fadden 的 cmets 后,我能够更有效地进行搜索。
看来我的错误是在动画线程中尝试调用onDraw()
。当我将那个电话改为postInvalidate()
时,它立即解决了问题。简而言之,动画循环的内部是这样的:
holder = view.getHolder();
canvas = holder.lockCanvas();
if (canvas != null)
synchronized (holder)
if (view != null)
view.DEBUG_THREADCALLS++;
// NOT view.onDraw(), but:
view.postInvalidate();
据我了解,对onDraw()
的调用会导致问题,因为它会导致应用同时绘制View
和Surface
。
我猜这个问题只出现在更现代的 Android 版本中的原因可能是因为更新的设备具有更高的刷新率,所以问题更明显。我注意到该解决方案还显着提高了我最旧设备上的帧速率,这可能是因为它无法同时绘制两个位置。
更新
根据下面非常有用的 cmets 的进一步建议,我完全删除了 SurfaceView,现在正在使用单独的动画线程在自定义视图上绘图。结果是即使在旧设备上也能获得令人难以置信的流畅动画效果,比 SurfaceView 好得多!因此,对于尝试使用 SurfaceView 进行动画处理的其他人,我说:除非您有充分的理由,否则不要这样做!
【讨论】:
由于您没有使用canvas
,您可以删除除view.postInvalidate()
之外的所有代码。
哦,哇。所以从字面上看,我需要做的就是创建一个单独的线程来发布无效?不费吹灰之力锁定画布?为什么这么多教程都像我以前那样使用 onDraw...?
很多SurfaceView教程都是完全错误的。 (这提醒了我,您现在可以将 SurfaceView 更改为 View,因为您根本不使用 Surface。)
这不会导致更生涩的动画吗?我认为我使用 SurfaceView 的最初原因是因为在 Android 规范中的某处提到使用 SurfaceView 更好地完成高帧率动画。无论如何,我会试一试,看看会发生什么。你是怎么知道这么多关于这些东西的?你能推荐一些好的资源吗?还是只是经验?
我曾在 Google 工作(点击我的名字查看我的个人资料)。我建议阅读 source.android.com/devices/graphics/architecture.html 以了解这些部分是如何组合在一起的。以上是关于SurfaceView drawText 闪烁的主要内容,如果未能解决你的问题,请参考以下文章