当前位置: 移动互联网学院 > Android开发 > SurfaceView绘图
SurfaceView绘图 时间:2017-06-15     来源:移动互联网学院

通常情况下的UI绘制和响应都是在Android的主线程中进行,比如Button、TextView等控件,但是这些动作都是非常轻量级的。有时候可能需要大量的绘图动作,这个时候仍然在主线程中去执行可能就会阻塞造成ANR现象的出现。那么如何在子线程中进行绘制呢?这个时候就需要用到SurfaceView来避免上述情况的发生。

SurfaceView拥有独立于主窗口的绘制图层,并且可以在子线程中进行大量的图像图形绘制,比如一些游戏、视频播放和拍照都是基于SurfaceView的这种机制上去实现的,从而避免主线程因为这些耗时操作造成不必要的各种麻烦。

简单来说实现SurfaceView需要继承SurfaceView类并实现其操作类SurfaceHolder下的Callback接口。

继承SurfaceView需要实现的方法如下:

(1) public void surfaceCreated(SurfaceHolder holder)

SurfaceView创建时的回调方法。

(2) public void surfaceChanged(SurfaceHolder holder,int format,int width,int height)

SurfaceView改变时的回调方法。

(3) public void surfaceDestroyed(SurfaceHolder holder)

SurfaceView销毁时的回调方法。

SurfaceHolder必须要实现addCallback(SurfaceHolder.Callback callback)方法,给操作类一个回调对象。

完成上述框架后,一个SurfaceView的框架就搭建好了,后只要在相关的回调中开启子线程进行相应的UI绘制即可。

下面一个例子,来介绍具体的使用方法。

首先是自定义的SurfaceView的子类MySurfaceView:

public class MySurfaceView extends SurfaceView

        implements SurfaceHolder.Callback {

    private SurfaceHolder mHolder;

    private RefreshViewThread mThread;

    public MySurfaceView(Context context) {

        super(context);

        mHolder = getHolder();

        mHolder.addCallback(this);

        //建立一个绘制UI的子线程,将SurfaceView的操作类SurfaceHolder的对象作为参数传入。

        mThread = new RefreshViewThread(mHolder);

    }

    @Override

    public void surfaceCreated(SurfaceHolder holder) {

        //当创建SurfaceView后开启绘制UI的子线程

        mThread.start();

    }

    @Override

    public void surfaceChanged(SurfaceHolder holder, int format, int width,

            int height) {

    }

    @Override

    public void surfaceDestroyed(SurfaceHolder holder) {

        mThread.mFlag = false;

    }

}

下面是UI绘制的子线程实现类RefreshViewThread:

public class RefreshViewThread extends Thread {

    private SurfaceHolder mHolder;

    // 控制线程是否持续运行的标志位

    public boolean mFlag = false;

    private int mCount = 0;

    private Canvas mCanvas;

    public RefreshViewThread(SurfaceHolder holder) {

        mHolder = holder;

        mFlag = true;

    }

    @Override

    public void run() {

        try {

            while (mFlag) {

                // 锁定画布,返回一个Canvas对象,利用其可以进行绘制

                mCanvas = mHolder.lockCanvas();

                mCanvas.drawColor(Color.GRAY);

                Paint paint = new Paint();

                paint.setColor(Color.BLUE);

                // 在画布上绘制子线程绘制的次数

                mCanvas.drawText("Run times: " + mCount, 300, 300, paint);

                // 为了效果看的更显著,休眠一秒

                Thread.sleep(1000);

                // 当绘制完成后要释放画布,并且将上述绘制的效果提交

                mHolder.unlockCanvasAndPost(mCanvas);

                if (mCount < 5) {

                    mCount++;

                } else {

                    mFlag = false;

                    mCount = 0;

                    mCanvas = null;

                }

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

这样一个高效的SurfaceView通过子线程绘制UI的功能就实现了。尽管SurfaceView的性能优良,但是它只适合主动的绘制,即用户不进行操作屏幕依然在绘制的情况下,比如市面上的大多数动作游戏。有的场合下,只有用户进行触屏或者键盘输入后,UI才会有刷新绘制的过程,这种情况下还是使用View在主线程中绘制更节省手机的性能消耗。