Android SurfaceView类的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/WannerWang/article/details/51043725

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对象来记录手指滑动的路径来进行绘图,在SurfaceViewonTouchEvent中来记录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操作,尽可能的节省系统资源。

运行效果如下:


展开阅读全文

没有更多推荐了,返回首页