如何在SurfaceView中冻结动画的最后一帧



我正在使用一个具有以下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_。我习惯于将术语WindowSurface互换使用,因为Surface基本上是事物的原生方的ANativeWindow

我认为发生了两件事中的一件,首先(非常不可能)你可能意外地重复绘制了动画的最后一帧,如果没有,那么这意味着你的SurfaceView不会因为视图上的任何更改而无效,因此保持缓存显示的最后一个像素缓冲区,以便测试它,在屏幕上设置一个按钮,当SurfaceView被"冻结"时,点击按钮并使按钮本身消失,这样你就会强制重新绘制屏幕上的所有元素,如果动画消失意味着它实际上被缓存了。。。

问候!

只有当应用程序曲面发生更改时,才能重新绘制。这很有效。但是——这就是Android文档所指的——任何其他应用程序、服务、Toast或其他任何东西都可能在一段时间内覆盖你的部分表面。以Service中的Toast为例。Toast消失时,您的应用程序和SurfaceView必须更新屏幕的该部分。但在代码中,只要没有设置标志,就不会进行更新。

最新更新