Android SurfaceView类的使用

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

运行效果如下:


Android,`SurfaceView`是一个非常有用的组件,用于处理用户界面的图形渲染,特别是对于游戏、动画等需要直接控制GPU的应用。下面是`SurfaceView`的基本使用步骤: 1. **创建SurfaceView**: 首先,在XML布局文件添加一个`SurfaceView`元素,并为其分配一个唯一的ID: ```xml <com.example.MyCustomView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 其,`MyCustomView`是你自定义的View,或者可以直接使用`SurfaceView`。 2. **初始化SurfaceView**: 在对应的Activity或Fragment获取SurfaceView的`SurfaceHolder`实例: ```java SurfaceView surfaceView = findViewById(R.id.surface_view); SurfaceHolder holder = surfaceView.getHolder(); ``` 3. **设置回调函数**: 设置`SurfaceHolder.Callback`来监听Surface的变化,包括`SurfaceCreated`、`SurfaceChanged`和`SurfaceDestroyed`: ```java holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { // 在这里初始化绘制环境 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 当Surface大小改变时,更新视图 } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 在Surface关闭时清理资源 } }); ``` 4. **绘制内容**: 在`surfaceCreated`方法,你可以开始创建你的图形上下文并开始绘制。这通常涉及到创建一个`Canvas`对象,然后使用OpenGL ES或其他图形API进行渲染。 5. **刷新视图**: 调用`holder.draw(canvas)`来提交你的绘制。如果需要定期更新画面,可以设置一个定时任务或者在一个循环不断调用`holder.update()`。 6. **注意内存管理**: `SurfaceView`可能需要较多的后台内存,因此确保及时释放不再使用的资源。 ```java @Override protected void onDestroy() { super.onDestroy(); if (holder != null) { holder.removeCallback(this); holder.destroySurface(); // 如果不再需要Surface,则销毁 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值