View與SurfaceView區(qū)別?

前言

在寫 SurfaceView之前当悔,先來記錄下 自定義View区丑,然后再引出 SurfaceView划栓;

1. 自定義View


針對于自定義View茧彤,我們需要知道以下幾點:

  • 自定義View 一般用來實現(xiàn)一些動畫效果常侦,是通過不斷執(zhí)行 View.onDraw()方法洋机,比如 onDraw()方法每秒執(zhí)行20次坠宴,會形成一個20幀的補間動畫,這里涉及到幀數(shù)的概念绷旗;
  • 幀數(shù):就是每秒onDraw()方法執(zhí)行的次數(shù)喜鼓;
  • onDraw()方法是系統(tǒng)幫我們調(diào)用,我們通過調(diào)用系統(tǒng)的 invalidate()方法 通知系統(tǒng)需要重新繪制 View衔肢,然后它就會調(diào)用 View.onDraw()方法庄岖,這些都是系統(tǒng)幫我們調(diào)用的;

基于以上角骤,我們很難準確定義 onDraw()方法的執(zhí)行幀數(shù)隅忿,這個時候就需要引入 SurfaceView,來彌補 自定義View的一些不足之處邦尊,下邊先來看一下我用 自定義View實現(xiàn)的一個動畫效果背桐,效果是從中間擴散到最大的圓,效果圖及代碼如下:


圖片.png
/**
 * Email: 2185134304@qq.com
 * Created by Novate 2018/5/7 13:25
 * Version 1.0
 * Params:
 * Description:    自定義View - 實現(xiàn)動畫效果
*/

public class AnimateViewActivity extends Activity {

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

        // 這邊傳入的this代表這個對象蝉揍,因為Activity是繼承自Content類的链峭,因此該對象也
        // 可向上轉(zhuǎn)型為Content類型作為AnimateView的構(gòu)造方法的參數(shù)
        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();//通過調(diào)用這個方法讓系統(tǒng)自動刷新視圖

        }

    }
}

2. 對自定義 View實現(xiàn)的動畫效果總結(jié)


  • 因為自定義View的幀數(shù)是有系統(tǒng)控制的又沾,所以采用自定義View 實現(xiàn)的動畫不能控制 動畫執(zhí)行的速度弊仪;
  • 可以把自定義View理解為:經(jīng)過系統(tǒng)優(yōu)化熙卡、可以高效執(zhí)行的幀數(shù)比較低的動畫效果,也就是說它有其具體的使用場景励饵,比如一些幀數(shù)比較低的游戲:貪吃蛇驳癌、棋牌類、俄羅斯方塊等

這個時候我們就需要一些幀數(shù)比較快的曲横、可以自己控制幀數(shù)的對象喂柒,因此SurfaceView就橫空出世了;

3. SurfaceView介紹


  • 可以控制幀數(shù)禾嫉;
  • 自定義VIew的更新UI灾杰,只能放在主線程中,SurfaceView可以讓其他線程更新UI熙参,根據(jù)這個特性艳吠,就可以控制它的幀數(shù),如果讓這個線程1秒執(zhí)行50次孽椰,最后就是顯示50幀

4. SurfaceView具體步驟如下


1>:SurfaceView也是一個View昭娩,它有自己的生命周期,surfaceCreate()黍匾、surfaceChange()栏渺、surfaceDestroy()這3個方法;
2>:可以在自定義DemoSurfaceView的構(gòu)造方法中開一個 LoopThread線程锐涯,然后進行繪制自定義View磕诊;
3>:在生命周期結(jié)束時,也就是 surfaceDestroy()方法中結(jié)束線程纹腌;

其實上邊這些都是由 其SurfaceHolder對象完成的霎终。SurfaceHolder保存了Surface對象的引用SurfaceHolder生命周期就是 Surface的生命周期,使用SurfaceHolder處理生命周期的初始化升薯;

具體代碼如下:

/**
 * Email: 2185134304@qq.com
 * Created by Novate 2018/5/7 13:40
 * Version 1.0
 * Params:
 * Description:    自定義的SurfaceView - 用于實現(xiàn)動畫效果
*/

public class DemoSurfaceView extends SurfaceView implements Callback{

    LoopThread thread;

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

        //初始化,設置生命周期回調(diào)方法
        init();
    }


    public DemoSurfaceView(Context context,AttributeSet attrs){
        super(context , attrs);
        //初始化,設置生命周期回調(diào)方法
        init();
    }


    private void init(){

        SurfaceHolder holder = getHolder();
        //設置Surface生命周期回調(diào)
        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:通過標志位關(guān)閉LoopThread線程
        thread.isRunning = false;
        try {
            // 關(guān)閉LoopThread線程莱褒,這里使用join()方法
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 執(zhí)行繪制的繪制線程
     */
    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);
                        //通過它來控制幀數(shù)執(zhí)行一次繪制后休息50ms
                        Thread.sleep(50);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    surfaceHolder.unlockCanvasAndPost(c);
                }

            }

        }

        public void doDraw(Canvas c){
            // 注意2:SurfaceView特點:會保留之前繪制的圖像,這里需要首先清空上一次繪制的圖形
            // 自定義View不用手動清理涎劈,自定義View在調(diào)用onDraw()方法就已經(jīng)自動清除掉視圖中的東西
            c.drawColor(Color.BLACK);
            c.translate(200, 200);
            c.drawCircle(0,0, radius++, paint);

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

上邊代碼需要注意:

1>:為防止多個線程同時操作 同一個 Canvas對象广凸,需要添加同步鎖synchronize,并且在操作完成后需要釋放Canvas鎖责语;
2>:在調(diào)用 doDraw()執(zhí)行繪制時候炮障,因為 SurfaceView的特點,它會保留之前繪制的圖形坤候,需要先清空上一次繪制時留下的圖形胁赢;
3>:在surfaceDestroy()方法中關(guān)閉線程就ok;

5. View與SurfaceView的區(qū)別白筹?


1>:自定義View只能在主線程中 更新UI智末,不能再子線程中更新谅摄,而SurfaceView可以在任何線程中更新UI;
2>:SurfaceView可以控制幀數(shù)系馆,執(zhí)行動畫效率比View的高送漠;
3>:SurfaceView是放在最底層,可以在它上邊添加一些層由蘑,而且不能是透明的闽寡;
4>:SurfaceView定義和使用比自定義View復雜,占用資源比較多尼酿,一般情況用自定義View就行爷狈,實在不行,最后再考慮SurfaceView裳擎;

具體代碼已上傳至github:
https://github.com/shuai999/SurfaceViewDemo.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涎永,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鹿响,更是在濱河造成了極大的恐慌羡微,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惶我,死亡現(xiàn)場離奇詭異妈倔,居然都是意外死亡,警方通過查閱死者的電腦和手機绸贡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門启涯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恃轩,你說我怎么就攤上這事±枳觯” “怎么了叉跛?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蒸殿。 經(jīng)常有香客問我筷厘,道長,這世上最難降的妖魔是什么宏所? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任酥艳,我火速辦了婚禮,結(jié)果婚禮上爬骤,老公的妹妹穿的比我還像新娘充石。我一直安慰自己,他們只是感情好霞玄,可當我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布骤铃。 她就那樣靜靜地躺著拉岁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惰爬。 梳的紋絲不亂的頭發(fā)上喊暖,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天,我揣著相機與錄音撕瞧,去河邊找鬼陵叽。 笑死,一個胖子當著我的面吹牛丛版,可吹牛的內(nèi)容都是我干的巩掺。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼硼婿,長吁一口氣:“原來是場噩夢啊……” “哼锌半!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寇漫,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤刊殉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后州胳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體记焊,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年栓撞,在試婚紗的時候發(fā)現(xiàn)自己被綠了遍膜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓤湘,死狀恐怖瓢颅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情弛说,我是刑警寧澤挽懦,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站木人,受9級特大地震影響信柿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜醒第,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一渔嚷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稠曼,春花似錦形病、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搀罢。三九已至,卻和暖如春侥猩,著一層夾襖步出監(jiān)牢的瞬間榔至,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工欺劳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留唧取,地道東北人。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓划提,卻偏偏與公主長得像枫弟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鹏往,可洞房花燭夜當晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內(nèi)容