Android View之SurfaceView优点

View更新UI问题

对于一个View的onDraw方法,不能这样做,因为从后台线程修改一个GUI元素会被显式地禁止的。
当需要快速地更新View的UI,或者当渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。

SurfaceView和View的区别

  • 本质区别
    surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。

  • 为什么普通View只能在主线程中更新UI?

    • UI线程是最重要的线程,负责绘制界面和分发窗口事件,它既不能被阻塞,也不是线程安全的。
    • 通常界面绘制和分发事件本该是多线程访问的,但是多线程处理时为了保证访问资源的正确性,对于某些操作都会加上同步锁,这样会显然会降低效率,而且还会涉及到线程的等待与线程上下文切换。
    • 为了提高效率,UI线程不在使用这些繁琐的多线程机制,为了保证对UI操作的正确性,只允许在UI线程中操作UI。在非UI线程中可通过post或者runOnUiThread来刷新view。
    • UI主线程是多线程访问不安全的,因此更新画面时可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。
  • SurfaceView子线程更新UI带来问题
    事件同步问题。比如你触屏了一下,你需要surfaceView中 thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。

  • 两种实例情况

    1. 画面依赖于onTouch来更新的,不需要一直更新,且更新效果简单,耗费资源少,这一次touch和下一次touch时间间隔比较长,可以直接用invalidate更新即可。

    2. 画面一直在更新,比如一个人在跑步,为了避免阻塞main UI thread这样就需要单独的thread不停地重绘ui了,这就要用到SurfaceView。

SurfaceView原理

  • 什么是Surface?
    简单地说Surface对应了一块屏幕缓冲区,每个window对应一个Surface,任何View都是画在Surface上的,传统的view共享一块屏幕缓冲区,所有的绘制必须在UI线程中进行

  • Surface和SurfaceView的关系
    SurfaceView封装了一个Surface对象,而不是Canvas,因为Surface可以使用后台线程绘制。
    对于那些资源敏感的操作或者那些要求快速更新或者高速帧率的地方,例如,使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。

  • 什么是SurfaceHolder.CallBack?
    SurfaceHolder.Callback主要是当底层的Surface被创建、销毁或者改变时提供回调通知。
    由于绘制必须在surface被创建后才能进行,因此SurfaceHolder.Callback中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。
    SurfaceHolder,可以把它当成Surface的容器和控制器,用来操纵Surface,处理它的Canvas上画的效果和动画,控制表面,大小,像素等。

  • 使用代价
    独立于GUI线程进行绘图的代价是额外的内存消耗,所以,虽然它是创建定制的View的有效方式–有时甚至是必须的,但是使用Surface View的时候仍然要保持谨慎。

  • 生命周期
    SurfaceView变得可见时,Surface被创建;SurfaceView隐藏前,Surface被销毁。这样能节省资源。

  • 应该注意
    所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。

创建SurfaceView

  • 要创建一个新的SurfaceView,需要创建一个新的扩展了SurfaceView的类,并实现SurfaceHolder.Callback。因为绘制工作在子线程中进行,模板代码一般会直接实现Runnable接口。
    如果是比较复杂的情况,那可能就需要使用线程池,任务队列进行处理了。因为两次事件触发间隔时间可能比一次事件处理时间还短,为保证响应正确性,一般用任务队列处理。

  • SurfaceHolder回调可以在底层的Surface被创建和销毁的时候通知View,并传递给它对SurfaceHolder对象的引用,其中包含了当前有效的Surface。

  • 一个典型的SurfaceView设计模型包括一个由Thread所派生的类,它可以接收对当前的SurfaceHolder的引用,并独立地更新它。

使用场景

  • SurfaceView使用的方式与任何View所派生的类都是完全相同的。可以像其他View那样应用动画,并把它们放到布局中。

  • SurfaceView封装的Surface支持使用本章前面所描述的所有标准Canvas方法进行绘图,同时也支持完全的OpenGL ES库。

  • 使用OpenGL,你可以再Surface上绘制任何支持的2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法可以依靠硬件加速(可用的时候)来极大地提高性能。

  • 对于显示动态的3D图像来说,例如,那些使用Google Earth功能的应用程序,或者那些提供沉浸体验的交互式游戏,SurfaceView特别有用。它还是实时显示摄像头预览的最佳选择。

简单画图板代码

package com.hcsys.display.views;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.EventLog;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * des:
 * author: admin
 * time: 2016/10/24 11:32.
 * e-mail:guohaichang@163.com
 */
public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    // SurfaceHolder
    private SurfaceHolder mHolder;
    // 用于绘图的Canvas
    private Canvas mCanvas;
    // 子线程标志位
    private volatile boolean mIsDrawing;
    //画笔
    private Paint mPaint;
    //绘制路径
    private Path mPath;

    public CustomSurfaceView(Context context) {
        super(context);
        initView();
    }

    public CustomSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public CustomSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
        //mHolder.setFormat(PixelFormat.OPAQUE);
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(10);
        mPaint.setAntiAlias(true);//抗锯齿
        mPath = new Path();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        while (mIsDrawing) {
            draw();
        }
        long end = System.currentTimeMillis();
        // 50 - 100
        if (end - start < 100) {//保证线程运行时间不少于100ms
            try {
                Thread.sleep(100 - (end - start));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(mPath,mPaint);
        } catch (Exception e) {
        } finally {
            if (mCanvas != null){
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(),event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(event.getX(),event.getY());
                break;
            case MotionEvent.ACTION_UP:

                break;
        }
        return true;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容