SurfaceView介绍和通用模板

本文深入解析了SurfaceView的工作原理及其与普通View的区别。介绍了SurfaceView如何适用于需要频繁刷新的场景,并提供了一个具体的SurfaceView模板实现,包括子线程绘制和触摸事件处理的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题汇总:

  1. SurfaceView是什么
  2. SurfaceView和View的区别
  3. SurfaceView怎么用

SurfaceView是什么


View绘制时,是通过VSYNC信号进行刷新,也就是16ms刷新一次。如果需要大量的操作导致16ms内无法绘制完成就会出现卡顿。在这种情况下SurfaceView就诞生了。

SurfaceView和View的区别


SufaceView适合处理逻辑多,刷新频繁的情况。View适用于一般大部分需求。

SufaceViewView
被动刷新(适用于频繁刷新)主动刷新
底层实现双缓冲机制无双缓冲
子线程中刷新画面主线程中刷新

SurfaceView模板


SurfaceView有一套模板,可以直接套用,具体意义直接看注释,是很详细的。

/**-----------------------------------------
 *   surfaceview模板:
 *    1.需要继承SurfaceView
 *    2.实现SurfaceHolder.Callback, Runnable
 * -----------------------------------------*/
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable{
    //SurfaceHolder
    protected SurfaceHolder mHolder;
    //Canvas:用于绘图
    protected Canvas mCanvas;
    //子线程标志位
    protected boolean mIsDrawing;

    public SurfaceViewTemplate(Context context){
        super(context);
        initView(); //初始化
    }
    public SurfaceViewTemplate(Context context, AttributeSet attrs){
        super(context, attrs);
        initView();
    }
    public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
        initView();
    }

    /*
    *  初始化
    * */
    private void initView(){
        mHolder = getHolder();
        mHolder.addCallback(this); //注册SurfaceHolder的回调方法
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
    }

    //SurfaceView创建
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true; //开启子线程
        new Thread(this).start(); //在子线程中通过while不断绘制
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false; //停止绘制(退出子线程循环)
    }

    /*-----------------------------------------------
    *  子线程中不断绘制(while):
    *  因为画面不需要过多刷新,浪费性能。所以适当进行sleep以此节省性能。
    *----------------------------------------------------*/
    @Override
    public void run() {
        long startTime;
        long endTime;
        while(mIsDrawing){
            startTime = System.currentTimeMillis();
            draw();//绘制
            endTime = System.currentTimeMillis();
            //测量一次draw的时间,并以此sleep一定时长
            //取值范围一般是50~100ms
            if(endTime - startTime < 50){
                try {
                    Thread.sleep((50-(endTime-startTime))); //睡满直至100ms
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /*------------------------------------
    *  绘制:
    *  1.lockCanvas()获得当前canvas对象, 每次获得的都是上次的Canvas对象, 绘制操作会保留。
    *    需要擦除,再回之前通过drawColor清屏
    *  2.unlockCanvasAndPost(mCanvas); 对画布内容进行提交
    *  3.将unlockCanvasAndPost放入finally确保每次都会提交
    *-----------------------------------*/
    private void draw(){
        try {
            mCanvas = mHolder.lockCanvas(); //获得当前canvas对象

            //draw something
        }catch (Exception e){

        }finally {
            if(mCanvas != null){
                mHolder.unlockCanvasAndPost(mCanvas); //对画布内容进行提交
            }
        }
    }
}

实例:写字板


写字板:用手在屏幕上写字,并且显示出来,就像电子黑板一样。
思路是用path在onTouchEvent中记录用户点击路径,并用paint按照path在canvas上进行绘制。这里直接继承SurfaceView模板,并且重新实现run()和draw()。

public class DrawBoardView extends SurfaceViewTemplate {
    Path mPath = new Path();
    public DrawBoardView(Context context) {
        super(context);
    }

    public DrawBoardView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public DrawBoardView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @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;
    }

    public void run() {
        long startTime;
        long endTime;
        while(mIsDrawing){
            startTime = System.currentTimeMillis();
            draw();
            endTime = System.currentTimeMillis();
            //取值范围一般是50~100ms
            if(endTime - startTime < 50){
                try {
                    Thread.sleep((50-(endTime-startTime))); //睡满直至100ms
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas(); //获得当前canvas对象
            mCanvas.drawColor(Color.BLACK);
            Paint paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setStyle(Paint.Style.STROKE); //绘制一定宽度的实线
            paint.setStrokeWidth(8);
            mCanvas.drawPath(mPath, paint);
            //draw something
        } catch (Exception e) {

        } finally {
            if (mCanvas != null) {
                mHolder.unlockCanvasAndPost(mCanvas); //对画布内容进行提交
            }
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值