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操作,尽可能的节省系统资源。

运行效果如下:


更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. android上一些方法的区别和用法的注意事项
  5. android实现字体闪烁动画的方法
  6. Android(安卓)matrix 控制图片的旋转、缩放、移动
  7. Android中dispatchDraw分析
  8. 锁屏界面
  9. android ndk编译x264开源(用于android的ffmpeg中进行软编码)

随机推荐

  1. 一个3000万日活跃用户App的真实数据
  2. ubuntu 10.10 64Bit下编译android和andro
  3. Android(安卓)Mms专题之:信息发送流程
  4. Android:创建可穿戴应用 - 建立模拟器和创
  5. Android高效率编码-第三方SDK详解系列(二
  6. Android中实现推送方式
  7. Android性能优化笔记
  8. Android(安卓)快速实现 ViewPager 滑动页
  9. 前端系列——与众不同的移动端底部固定栏
  10. 2020年最新Android大厂面试题 只为你进BA