View与SurfaceView区别?

前言

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

1. 自定义View


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

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

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


图片.png
/**
 * Email: 2185134304@qq.com
 * 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: 2185134304@qq.com
 * 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 227,533评论 6 531
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,055评论 3 414
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 175,365评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,561评论 1 307
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,346评论 6 404
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,889评论 1 321
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,978评论 3 439
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,118评论 0 286
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,637评论 1 333
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,558评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,739评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,246评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,980评论 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,362评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,619评论 1 280
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,347评论 3 390
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,702评论 2 370

推荐阅读更多精彩内容