通常情况下的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在主线程中绘制更节省手机的性能消耗。
热点新闻