Android画布多次绘制会导致闪烁

时间:2016-03-18 14:04:04

标签: android android-canvas surfaceview flicker

关于此主题的关键字:

  1. CustomSurfaceView:三个不同级别的三个自定义surfaceview。
  2. Canvas:lock / unlockAndPost方法(我不使用自定义位图)
  3. 多线程(每个表面是一个单独的线程)
  4. 形状(画布上的形状)
  5. 客户端/服务器(架构)
  6. 闪烁(我为什么在这里)
  7. 我们正在开发客户端/服务器应用程序,我正在客户端工作。我正在接收来自服务器的消息,其中包含有关路径的常规数据(坐标,颜色,宽度[...]),如圆形,矩形,线形和其他形状。 Web应用程序允许用户将这些绘制在 HTML5 Canvas 上的数据发送到接收此消息并解析它的Android设备,将能够重绘所有形状。根据我自己在这个主题上的经验,我了解到控制画布上绘制的所有内容的最佳方法是将所有内容保存到缓冲区,数组,列表或类似内容中,然后在需要时重复使用(对于例如,您可以使用较旧的路径来显示,隐藏,移动或只是在画布上更改某些内容。在我看来,Android应用程序遵循Android开发和OOP范例的最佳实践,所以我不假设与坏架构相关的错误。在这种情况下,我将在Web客户端保存消息。当用户使用HTML5 Canvas时,包含形状信息的消息会完美地报告给Android画布,但问题出现在:

    【示例】 考虑您绘制10个对象(10条消息),并且您希望在Web应用程序画布上仅删除一个对象,因此唯一的方法是清除所有画布,并重绘所有以前的形状而不删除形状(因此通过循环消息缓冲区重新发送到客户端9消息)。此方法适用于Web应用程序,但导致Android客户端闪烁问题。所以在经过太多实验后,我找到了一个解决方法,使用Thread.sleep(100)(Whooo!100ms太多),以便缓慢解析消息并让surfaceview线程正确读取数据(通过单例模式访问数据)并写在画布的双缓冲区上。好吧,它既慢又丑,但它有效!其实我不喜欢这种“可怕”的解决方法,所以请帮我看一下退出策略。

    这是一段代码,其中画布从形状容器中获取数据并绘制数据是否存在。每个容器的数据来自服务器消息。

    @Override
    public void run() {
        Canvas canvas = null;
        while (running) {
            //this is the surface's canvas
            try {
                canvas = shapesSurfaceHolder.lockCanvas();
                synchronized (shapesSurfaceHolder) {
                    if (shapesSurfaceHolder.getSurface().isValid()) {
                        if(!Parser.cmdClear){
    
                            //draw all the data present
                            canvas.drawPath(PencilData.getInstance().getPencilPath(),
                                    PencilData.getInstance().getPaint());
                            canvas.drawPath(RectData.getInstance().getRectPath(),
                                    RectData.getInstance().getPaint());
                            canvas.drawPath(CircleData.getInstance().getCirclePath(),
                                    CircleData.getInstance().getPaint());
                            canvas.drawPath(LineData.getInstance().getLinePath(),
                                    LineData.getInstance().getPaint());
                            canvas.drawText(TextData.getInstance().getText(),
                                    TextData.getInstance().getX(),
                                    TextData.getInstance().getY(),
                                    TextData.getInstance().getPaint());
    
                        } else {
                            //remove all canvas content and clear data.
                            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                            for (int i = 0; i < AbstractFactory.SHAPE_NUM; i++) {
                                abstracFactory.getShape(i).clearData();
                            }
                        }
                    }
                }
            } finally {
                if (canvas != null) {
                    shapesSurfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }//end_run()
    

    我可以总结一下,显然,我的问题是画得太快

    注意:

    类似的概念:Android thread controlling multiple texture views causes strange flickering

    启用硬件加速。

    minSdkVersion 17

    经过测试

    • 平板电脑三星SM-T113

    • Google Nexus 5

1 个答案:

答案 0 :(得分:2)

TextureView问题是由于TextureView特有的错误造成的。您正在使用SurfaceView,因此这里不适用。

在SurfaceView的Surface上绘图时,每次都必须更新脏矩形内的每个像素(即传递给lockCanvas()的可选arg)。如果您没有提供脏矩形,则表示必须更新整个屏幕。这是因为Surface是双缓冲或三缓冲的,并在您调用unlockCanvasAndPost()时进行交换。如果您锁定/清除/解锁,则下次锁定/绘制/解锁时,您将不会进入先前清除的缓冲区。

如果要进行增量渲染,则应将Canvas指向屏幕外的位图并在那里进行所有渲染。然后只需在锁定和解锁之间blit整个位图。另一种方法是存储绘图命令,从初始清除开始,然后在锁定/解锁之间回放它们。

短语&#34;三个自定义表面视图&#34;如果他们一次全屏在屏幕上就有点担心。如果你将它们全部放在不同的Z深度(默认,媒体覆盖,顶部),那么它们将表现正常,但如果你可以把所有东西放在一个系统上,那么系统效率通常会更高。

相关问题