View与SurfaceView区别?

前言

在写 SurfaceView之前,先来记录下 自定义View,然后再引出 SurfaceView;

1. 自定义View


针对于自定义View,我们需要知道以下几点:

  • 自定义View 一般用来实现一些动画效果,是通过不断执行 View.onDraw()方法,比如 onDraw()方法每秒执行20次,会形成一个20帧的补间动画,这里涉及到帧数的概念;
  • 帧数:就是每秒onDraw()方法执行的次数;
  • onDraw()方法是系统帮我们调用,我们通过调用系统的 invalidate()方法 通知系统需要重新绘制 View,然后它就会调用 View.onDraw()方法,这些都是系统帮我们调用的;

基于以上,我们很难准确定义 onDraw()方法的执行帧数,这个时候就需要引入 SurfaceView,来弥补 自定义View的一些不足之处,下边先来看一下我用 自定义View实现的一个动画效果,效果是从中间扩散到最大的圆,效果图及代码如下:


View与SurfaceView区别?_第1张图片
图片.png
/**
 * Email: [email protected]
 * Created by Novate 2018/5/7 13:25
 * Version 1.0
 * Params:
 * Description:    自定义View - 实现动画效果
*/

public class AnimateViewActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 這邊傳入的this代表這個對象,因為Activity是繼承自Content類的,因此該對象也
        // 可向上轉型為Content類型作為AnimateView的構造方法的參數
        setContentView(new AnimateView(this));

    }

    class AnimateView extends View {

        float radius = 10;
        Paint paint;

        public AnimateView(Context context) {
            super(context);
            paint = new Paint();
            paint.setColor(Color.GREEN);  // 圆圈颜色
            paint.setStyle(Paint.Style.STROKE); // 线条模式  文字、图像只显示边界
            paint.setStrokeWidth((float) 10.0); // 宽度
        }

        @Override
        protected void onDraw(Canvas canvas) {

            canvas.translate(200, 200);
            canvas.drawCircle(0, 0, radius++, paint);

            if(radius > 100){
                radius = 10;
            }

            invalidate();//通过调用这个方法让系统自动刷新视图

        }

    }
}

2. 对自定义 View实现的动画效果总结


  • 因为自定义View的帧数是有系统控制的,所以采用自定义View 实现的动画不能控制 动画执行的速度;
  • 可以把自定义View理解为:经过系统优化、可以高效执行的帧数比较低的动画效果,也就是说它有其具体的使用场景,比如一些帧数比较低的游戏:贪吃蛇、棋牌类、俄罗斯方块等

这个时候我们就需要一些帧数比较快的、可以自己控制帧数的对象,因此SurfaceView就横空出世了;

3. SurfaceView介绍


  • 可以控制帧数;
  • 自定义VIew的更新UI,只能放在主线程中,SurfaceView可以让其他线程更新UI,根据这个特性,就可以控制它的帧数,如果让这个线程1秒执行50次,最后就是显示50帧

4. SurfaceView具体步骤如下


1>:SurfaceView也是一个View,它有自己的生命周期,surfaceCreate()、surfaceChange()、surfaceDestroy()这3个方法;
2>:可以在自定义DemoSurfaceView的构造方法中开一个 LoopThread线程,然后进行绘制自定义View;
3>:在生命周期结束时,也就是 surfaceDestroy()方法中结束线程;

其实上边这些都是由 其SurfaceHolder对象完成的。SurfaceHolder保存了Surface对象的引用SurfaceHolder生命周期就是 Surface的生命周期,使用SurfaceHolder处理生命周期的初始化;

具体代码如下:

/**
 * Email: [email protected]
 * Created by Novate 2018/5/7 13:40
 * Version 1.0
 * Params:
 * Description:    自定义的SurfaceView - 用于实现动画效果
*/

public class DemoSurfaceView extends SurfaceView implements Callback{

    LoopThread thread;

    public DemoSurfaceView(Context context) {
        super(context);

        //初始化,设置生命周期回调方法
        init();
    }


    public DemoSurfaceView(Context context,AttributeSet attrs){
        super(context , attrs);
        //初始化,设置生命周期回调方法
        init();
    }


    private void init(){

        SurfaceHolder holder = getHolder();
        //设置Surface生命周期回调
        holder.addCallback(this);

        // LoopThread线程用于绘制图形
        thread = new LoopThread(holder, getContext());
    }

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

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        thread.isRunning = true;
        thread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // 注意3:通过标志位关闭LoopThread线程
        thread.isRunning = false;
        try {
            // 关闭LoopThread线程,这里使用join()方法
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行绘制的绘制线程
     */
    class LoopThread extends Thread{

        SurfaceHolder surfaceHolder;
        Context context;
        boolean isRunning;
        float radius = 10f;
        Paint paint;

        public LoopThread(SurfaceHolder surfaceHolder,Context context){

            this.surfaceHolder = surfaceHolder;
            this.context = context;
            isRunning = false;

            paint = new Paint();
            paint.setColor(Color.GREEN);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth((float) 10.0); // 宽度
        }

        @Override
        public void run() {
            Canvas c = null;
            while(isRunning){
                try{
                    // 注意1:同步锁为了防止多个线程同时操作同一个Canvas的c
                    synchronized (surfaceHolder) {
                        c = surfaceHolder.lockCanvas(null);
                        doDraw(c);
                        //通过它来控制帧数执行一次绘制后休息50ms
                        Thread.sleep(50);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    surfaceHolder.unlockCanvasAndPost(c);
                }

            }

        }

        public void doDraw(Canvas c){
            // 注意2:SurfaceView特点:会保留之前绘制的图像,这里需要首先清空上一次绘制的图形
            // 自定义View不用手动清理,自定义View在调用onDraw()方法就已经自动清除掉视图中的东西
            c.drawColor(Color.BLACK);
            c.translate(200, 200);
            c.drawCircle(0,0, radius++, paint);

            if(radius > 100){
                radius = 10f;
            }
        }
    }
}

上边代码需要注意:

1>:为防止多个线程同时操作 同一个 Canvas对象,需要添加同步锁synchronize,并且在操作完成后需要释放Canvas锁;
2>:在调用 doDraw()执行绘制时候,因为 SurfaceView的特点,它会保留之前绘制的图形,需要先清空上一次绘制时留下的图形;
3>:在surfaceDestroy()方法中关闭线程就ok;

5. View与SurfaceView的区别?


1>:自定义View只能在主线程中 更新UI,不能再子线程中更新,而SurfaceView可以在任何线程中更新UI;
2>:SurfaceView可以控制帧数,执行动画效率比View的高;
3>:SurfaceView是放在最底层,可以在它上边添加一些层,而且不能是透明的;
4>:SurfaceView定义和使用比自定义View复杂,占用资源比较多,一般情况用自定义View就行,实在不行,最后再考虑SurfaceView;

具体代码已上传至github:
https://github.com/shuai999/SurfaceViewDemo.git

你可能感兴趣的:(View与SurfaceView区别?)