我正在使用一个具有以下calssic run()方法的线程在SurfaceView上绘制:
@Override
public void run() {
while (mRun) {
Canvas canvas = null;
synchronized (mSurfaceHolder) {
try {
canvas = mSurfaceHolder.lockCanvas();
update();
doDraw(canvas);
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
如果从那一刻起即将绘制的下一帧与上一帧完全相同,我想保存一些工作,所以我跳过了onDraw,但由于双重缓冲机制,我遇到了闪烁问题。然后我继续做了这样的事情:
@Override
public void run() {
while (mRun) {
Canvas canvas = null;
synchronized (mSurfaceHolder) {
if (mIsAnimating) { // <---- Look ma! A flag!!!
try {
canvas = mSurfaceHolder.lockCanvas();
update();
doDraw(canvas);
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
}
如果我关闭mIsAnimating标志,从而阻止我的运行循环锁定画布并在画布上绘制,那么一切都像一种魅力——我做了一些动画,完成后,我可以冻结绘制循环,并将动画的最后一帧保留在屏幕上。
但后来我不得不去看看Android文档中的以下内容:
曲面的内容从未在unlockCanvas()之间保留和lockCanvas(),因此,曲面区域内的每个像素必须书写。此规则的唯一例外是当指定矩形,在这种情况下,非脏像素将保存。
所以。。。我的代码是如何工作的?
你的问题很好。SurfaceView
是一种奇怪的动物。
首先,View
通常不会默认使用缓存绘图(据我所知,这仍然是真的),SurfaceView
也不会启用它。所以我有理由相信没有缓存。
SurfaceView
正在做的是在视图层次结构的Window
上挖一个洞,这样你就可以看到另一个Window
或直接在它后面的帧缓冲区上绘制的东西。安卓在他们的PorterDuff.Mode
枚举中添加了一个不存在的Porter-Duff模式来实现这种奇怪的行为。因此,普通绘图规则不适用于SurfaceView
。无效或请求布局只需再次剪切孔。视图层次结构不知道或不关心屏幕该区域中实际绘制的内容,因此,除非您以其他方式在其上绘制(例如,使用SurfaceHolder
为单独的Window
上的同一区域获取Canvas
),否则它很可能只是原封不动地放在那里。
因此,我真正认为正在发生的是,Android根本没有重新绘制帧缓冲区的那部分。从Surface
中提取的内容会保留在那里,而视图层次结构的其余部分则围绕着它绘制。Surface
的资源很可能会被释放,你永远不会知道。
我很好奇,如果你切换到不同的Activity
或主屏幕,然后回来会发生什么?我的猜测是,你的最后一帧将不再存在。如果是的话,那么SurfaceView
比我所知的还要陌生。。。
编辑:只是一个简短的提示。。。CCD_ 16基本上是具有一点额外功能的CCD_。我习惯于将术语Window
与Surface
互换使用,因为Surface
基本上是事物的原生方的ANativeWindow
。
我认为发生了两件事中的一件,首先(非常不可能)你可能意外地重复绘制了动画的最后一帧,如果没有,那么这意味着你的SurfaceView不会因为视图上的任何更改而无效,因此保持缓存显示的最后一个像素缓冲区,以便测试它,在屏幕上设置一个按钮,当SurfaceView被"冻结"时,点击按钮并使按钮本身消失,这样你就会强制重新绘制屏幕上的所有元素,如果动画消失意味着它实际上被缓存了。。。
问候!
只有当应用程序曲面发生更改时,才能重新绘制。这很有效。但是——这就是Android文档所指的——任何其他应用程序、服务、Toast或其他任何东西都可能在一段时间内覆盖你的部分表面。以Service
中的Toast
为例。Toast消失时,您的应用程序和SurfaceView
必须更新屏幕的该部分。但在代码中,只要没有设置标志,就不会进行更新。