您的位置:首页 > 移动开发 > Android开发

Android SurfaceView类的使用

2016-04-02 13:57 806 查看
Android提供了View进行绘图处理,View可以满足大部分绘图需求,但在某些时候,却也有心有余而力不足的地方,View是通过刷新来绘制视图,Android系统通过发出VSYNC信号来进行屏幕的重绘,刷新时间间隔为16ms。但是如果在16ms内完成所需执行的操作用户不会感觉到卡顿,而如果执行的操作逻辑太多,特别是频繁的刷新界面,就会不断的阻塞主线程,导致画面卡顿。

Android提供了SurfaceView组件来解决这一问题,其与View的主要区别是:

View主要适用于主动更新的情况下,SurfaceView主要适用于被动更新,例如频繁的刷新。

View在主线程中进行刷新,SurfaceView通常会通过一个子线程来进行页面的刷新。

View绘图时没有使用双缓冲机制,而SurfaceView在底层实现机制中就已经使用了双缓冲机制。

总而言之: 如果自定义的View需要频繁的刷新或刷新数据量较大时,就可以考虑使用SurfaceView。

SurfaceView的使用:

步骤一:

创建自定义的SurfaceView继承自SurfaceView,并实现两个接口SurfaceHolder.CallBack 和Runnable

如下:

public class SimpleDraw extends SurfaceView implements SurfaceHolder.Callback, Runnable

通过实现上两个接口,就需要在自定义的View中实现接口的方法,对于SurfaceHolder.CallBack需要实现如下方法:

@Override

public void surfaceCreated(SurfaceHolder holder) {//开启子线程进行绘制

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {// SurfaceView的改变

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) { // SurfaceView的销毁

}

对于Runnable需要实现run 方法。

二:初始化SurfaceView

在自定义的SurfaceView构造方法中,需要对SurfaceView进行初始化,在自定义的SurfaceView中通常需要定义三个变量。

private SurfaceHolder mholder;

private Canvas mCanvas;

private boolean mIsDrawing;// 子线程标志位

初始化就是对SurfaceHolder进行初始化,并且注册SurfaceHolder的回调方法 如下:

mholder= getHolder();

mholder.addCallback(this);

三: 使用SurfaceView :

通过SurfaceHolder对象的lockCanvas()方法,就可以获得当前Canvas绘图的对象,接下来就可以与在View中绘图一样进行绘制了,需注意的是获取到的Canvas对象,而不是一个新的对象。

绘制的时候,充分利用SurfaceView的三个回调方法,在surfaceCreated()方法中开启子线程中进行绘制,而使用while (mIsDrawing)的循环来不停的绘制,而在绘制的具体逻辑中通过lockCanvans ()方法获得Canvans对象进行绘制,并通过unlockCanvasAndPost (mCanvas)方法对画布内容进行提交。

需要注意的是

mholder.unlockCanvasAndPost(mCanvas); 方法放到finally中,保证每次都将内容提交。

实例:

使用SurfaceView实现画图板:

通过path对象来记录手指滑动的路径来进行绘图,在SurfaceView的onTouchEvent中来记录Path路径。

public class SimpleDraw extends SurfaceView implements SurfaceHolder.Callback, Runnable {

private Paint mpaint;

private Path mpath;

private SurfaceHolder mHolder;

private boolean misDrawing;

private Canvas mcanvas;

public SimpleDraw(Context context) {

super(context);

initView();

}

public SimpleDraw(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

initView();

}

public SimpleDraw(Context context, AttributeSet attrs) {

super(context, attrs);

initView();

}

public void initView() {

mHolder = getHolder();

mHolder.addCallback(this);

setFocusable(true);

setFocusableInTouchMode(true);

this.setKeepScreenOn(true);

mpath = new Path();

mpaint = new Paint();

mpaint.setColor(Color.RED);

mpaint.setStyle(Paint.Style.STROKE);

mpaint.setStrokeWidth(20);

}

@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 stratTime = System.currentTimeMillis();

while(misDrawing){

draw();

}

long endTime = System.currentTimeMillis();

if (endTime-stratTime<100){

try{

Thread.sleep(100-(endTime-stratTime));

}catch (InterruptedException e){

e.printStackTrace();

}

}

}

private 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) {

int x = (int) event.getX();

int y = (int) event.getY();

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

mpath.moveTo(x, y);

break;

case MotionEvent.ACTION_MOVE:

mpath.lineTo(x, y);

break;

case MotionEvent.ACTION_UP:

break;

}

return true;

}

}

以上代码需要注意的是,需要在子线程的循环中进行优化,没有必要一直调用draw()方法进行绘制,可以在子线程中进行sleep操作,尽可能的节省系统资源。

运行效果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: