Android中的SurfaceView详解

Android中提供了View进行绘图处理,View可以满足大部分的绘图需求,但是有时候,View却显得力不从心,所以Android提供了SurfaceView给Android开发者,以满足更多的绘图需求。下面就让我们一起来了解一下SurfaceView。

一、为什么要使用SurfaceView

我们知道View是通过刷新来重绘视图,系统通过发出VSSYNC信号来进行屏幕的重绘,刷新的时间间隔是16ms,如果我们可以在16ms以内将绘制工作完成,则没有任何问题,如果我们绘制过程逻辑很复杂,并且我们的界面更新还非常频繁,这时候就会造成界面的卡顿,影响用户体验,为此Android提供了SurfaceView来解决这一问题。

View和SurfaceView的区别:

  • 1 . View适用于主动更新的情况,而SurfaceView则适用于被动更新的情况,比如频繁刷新界面。
  • 2 . View在主线程中对页面进行刷新,而SurfaceView则开启一个子线程来对页面进行刷新。
  • 3 . View在绘图时没有实现双缓冲机制,SurfaceView在底层机制中就实现了双缓冲机制。

这摘录了一段网上对于双缓冲技术的介绍

双缓冲技术是游戏开发中的一个重要的技术。当一个动画争先显示时,程序又在改变它,前面还没有显示完,
程序又请求重新绘制,这样屏幕就会不停地闪烁。而双缓冲技术是把要处理的图片在内存中处理好之后,
再将其显示在屏幕上。双缓冲主要是为了解决 反复局部刷屏带来的闪烁。
把要画的东西先画到一个内存区域里,然后整体的一次性画出来。

二、如何使用SurfaceView

要想使用SurfaceView需要经过创建、初始化、使用三个步骤,下面我们就一步步来说说这三个步骤。

我们需要自定义一个类继承自SurfaceView,并且实现两个接口以及接口定义的方法,当然,与自定义View类似,还要重写三个构造函数。
下面是代码:
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    public SurfaceViewTemplate(Context context) {
        this(context, null);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
       //创建
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //改变
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        //销毁
    }

    @Override
    public void run() {
        //子线程
    }
}

前面三个构造函数的写法和自定义View是相同的,接下来的三个方法分别在SurfaceView创建、改变、销毁的时候进行调用,最后的run()方法中写我们子线程中执行的绘图逻辑即可。

2 . 初始化SurfaceView

这一步我们主要是定义三个成员变量以备后面绘图时使用,然后初始化这三个成员变量并且注册对应的回调方法。代码如下:

private SurfaceHolder mSurfaceHolder;
//绘图的Canvas
private Canvas mCanvas;
//子线程标志位
private boolean mIsDrawing;

/**
 * 初始化View
 */
private void initView(){
    mSurfaceHolder = getHolder();
    //注册回调方法
    mSurfaceHolder.addCallback(this);
    //设置一些参数方便后面绘图
    setFocusable(true);
    setKeepScreenOn(true);
    setFocusableInTouchMode(true);
}

public SurfaceViewSinFun(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //在三个参数的构造方法中完成初始化操作
    initView();
}

3 . 使用SurfaceView

经过上面两步的准备工作,下面就可以开始使用SurfaceView了。

这一步又可以分为3步来完成:
(1) 通过lockCanvas()方法获得Canvas对象
(2) 在子线程中使用Canvas对象进行绘制
(3) 使用unlockCanvasAndPost()方法将画布内容进行提交
注意: lockCanvas() 方法获得的Canvas对象仍然是上次绘制的对象,由于我们是不断进行绘制,但是每次得到的Canvas对象都是第一次创建的Canvas对象。

SurfaceView的绘制可以使用下面的模板代码来实现,唯一的不同就是绘制逻辑的不同,代码如下:

public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder mSurfaceHolder;
    //绘图的Canvas
    private Canvas mCanvas;
    //子线程标志位
    private boolean mIsDrawing;
    public SurfaceViewTemplate(Context context) {
        this(context, null);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    @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() {
        while (mIsDrawing){
            drawSomething();
        }
    }
    //绘图逻辑
    private void drawSomething() {
        try {
            //获得canvas对象
            mCanvas = mSurfaceHolder.lockCanvas();
            //绘制背景
            mCanvas.drawColor(Color.WHITE);
            //绘图
        }catch (Exception e){

        }finally {
            if (mCanvas != null){
                //释放canvas对象并提交画布
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    /**
     * 初始化View
     */
    private void initView(){
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        setFocusable(true);
        setKeepScreenOn(true);
        setFocusableInTouchMode(true);
    }
}

https://www.jianshu.com/p/b037249e6d31

上一篇:python模块之HTMLParser简介


下一篇:devise 自定义手机号登录