<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    咖啡伴侶

    呆在上海
    posts - 163, comments - 156, trackbacks - 0, articles - 2

    android中使用2D動畫 — SurfaceView 轉載

    Posted on 2011-07-21 09:45 oathleo 閱讀(2929) 評論(0)  編輯  收藏 所屬分類: Android

    android中使用2D動畫 — SurfaceView

     

    通過之前介紹的如何自定義View, 我們知道使用它可以做一些簡單的動畫效果。它通過不斷循環的執行View.onDraw方法,每次執行都對內部顯示的圖形做一些調整,我們假設 onDraw方法每秒執行20次,這樣就會形成一個20幀的補間動畫效果。但是現實情況是你無法簡單的控制View.onDraw的執行幀數,這邊說的執 行幀數是指每秒View.onDraw方法被執行多少次,這是為什么呢?首先我們知道,onDraw方法是由系統幫我們調用的,我們是通過調用View的 invalidate方法通知系統需要重新繪制View,然后它就會調用View.onDraw方法。這些都是由系統幫我們實現的,所以我們很難精確去定 義View.onDraw的執行幀數,這個就是為什么我們這邊要了解SurfaceView了,它能彌補View的一些不足。

    首先我們先寫一個自定義View實現動畫效果,AnimateViewActivity.java:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    package com.android777.demo.uicontroller.graphics;
     
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.os.Bundle;
    import android.view.View;
     
    public class AnimateViewActivity extends Activity {
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
     
            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.YELLOW);
                paint.setStyle(Paint.Style.STROKE);
            }
     
            @Override
            protected void onDraw(Canvas canvas) {
     
                canvas.translate(200, 200);
                canvas.drawCircle(0, 0, radius++, paint);          
     
                if(radius > 100){
                    radius = 10;
                }
     
                invalidate();//通過調用這個方法讓系統自動刷新視圖
     
            }
     
        }
     
    }

    運行上面的Activity,你將看到一個圓圈,它原始半徑是10,然后不斷的變大,直到達到100后又恢復到10,這樣循環顯示,視覺效果上說你將看到一個逐漸變大的圓圈。

    上面就是一個簡單的自定義View實現的動畫效果,它能做的只是簡單的動畫效果,具有一些局限性。首先你無法控制動畫的顯示速度,目前它是以最快的 速度顯示,但是當你要更快,獲取幀數更高的動畫呢? 因為View的幀數是由系統控制的,所以你沒辦法完成上面的操作。如果你需要編寫一個游戲,它需要的幀數比較高,那么View就無能為力了,因為它被設計 出來時本來就不是用來處理一些高幀數顯示的。你可以把View理解為一個經過系統優化的,可以用來高效的執行一些幀數比較低動畫的對象,它具有特定的使用 場景,比如有一些幀數較低的游戲就可以使用它來完成:貪吃蛇、俄羅斯方塊、棋牌類等游戲,因為這些游戲執行的幀數都很低。但是如果是一些實時類的游戲,如 射擊游戲、塔防游戲、RPG游戲等就沒辦法使用View來做,因為它的幀數太低了,會導致動畫執行不順暢。所以我們需要一個能自己控制執行幀數的對 象,SurfaceView因此誕生了。

    什么是SurfaceView呢?

    為什么是SurfaceView呢?Surface的意思是表層,表面的意思,那么SurfaceView就是指一個在表層的View對象。為什么 說是在表層呢,這是因為它有點特殊跟其他View不一樣,其他View是繪制在表層外,而它就是充當表層對象。假設你要在一個球上畫畫,那么球的表層就當 做你的畫布對象,你畫的東西會擋住它的表層,我們默認沒使用SurfaceView,那么球的表層就是空白的,如果我們使用了SurfaceView,我 們可以理解為我們拿來的球本身表面就具有紋路,你是畫再紋路之上的,如果你畫的是半透明的,那么你將可以透過你畫的東西看到球面本身的紋路。SDK的文檔 說到:SurfaceView就是在Window上挖一個洞,它就是顯示在這個洞里,其他的View是顯示在Window上,所以View可以顯式在 SurfaceView之上,你也可以添加一些層在SurfaceView之上。

    SurfaceView還有其他的特性,上面我們講了它可以控制幀數,那它是什么控制的呢?這就需要了解它的使用機制。一般在很多游戲設計中,我們都是開辟一個后臺線程計算游戲相關的數據,然后根據這些計算完的新數據再刷新視圖對象,由于對View執行繪制操作只能在UI線程上, 所以當你在另外一個線程計算完數據后,你需要調用View.invalidate方法通知系統刷新View對象,所以游戲相關的數據也需要讓UI線程能訪 問到,這樣的設計架構比較復雜,要是能讓后臺計算的線程能直接訪問數據,然后更新View對象那改多好。我們知道View的更新只能在UI線程中,所以使 用自定義View沒辦法這么做,但是SurfaceView就可以了。它一個很好用的地方就是允許其他線程(不是UI線程)繪制圖形(使用Canvas),根據它這個特性,你就可以控制它的幀數,你如果讓這個線程1秒執行50次繪制,那么最后顯示的就是50幀。

     

    如何使用SurfaceView?

    首先SurfaceView也是一個View,它也有自己的生命周期。因為它需要另外一個線程來執行繪制操作,所以我們可以在它生命周期的初始化階 段開辟一個新線程,然后開始執行繪制,當生命周期的結束階段我們插入結束繪制線程的操作。這些是由其內部一個SurfaceHolder對象完成的。 SurfaceHolder,顧名思義,它里面保存了一個隊Surface對象的引用,而我們執行繪制方法就是操作這個 Surface,SurfaceHolder因為保存了對Surface的引用,所以使用它來處理Surface的生命周期,說到底 SurfaceView的生命周期其實就是Surface的生命周期,因為SurfaceHolder保存對Surface的引用,所以使用 SurfaceHolder來處理生命周期的初始化。首先我們先看看建立一個SurfaceView的大概步驟,先看看代碼:

    DemoSurfaceView.java:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    package com.android777.demo.uicontroller.graphics;
     
    import android.content.Context;
    import android.view.SurfaceHolder;
    import android.view.SurfaceHolder.Callback;
    import android.view.SurfaceView;
     
    public class DemoSurfaceView extends SurfaceView  implements Callback{
     
        public DemoSurfaceView(Context context) {
            super(context);
     
            init(); //初始化,設置生命周期回調方法
     
        }
     
        private void init(){
     
            SurfaceHolder holder = getHolder();
            holder.addCallback(this); //設置Surface生命周期回調
     
        }
     
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
        }
     
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        }
     
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
        }
     
    }

    上面代碼我們在SurfaceView的構造方法中執行了init初始化方法,在這個方法里,我們先獲取SurfaceView里的 SurfaceHolder對象,然后通過它設置Surface的生命周期回調方法,使用DemoSurfaceView類本身作為回調方法代理類。 surfaceCreated方法,是當SurfaceView被顯示時會調用的方法,所以你需要再這邊開啟繪制的線 程,surfaceDestroyed方法是當SurfaceView被隱藏會銷毀時調用的方法,在這里你可以關閉繪制的線程。上面的例子運行后什么也不 顯示,因為還沒定義一個執行繪制的線程。下面我們修改下代碼,使用一個線程繪制一個逐漸變大的圓圈:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    package com.android777.demo.uicontroller.graphics;
     
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.view.SurfaceHolder;
    import android.view.SurfaceHolder.Callback;
    import android.view.SurfaceView;
     
    public class DemoSurfaceView extends SurfaceView  implements Callback{
     
        LoopThread thread;
     
        public DemoSurfaceView(Context context) {
            super(context);
     
            init(); //初始化,設置生命周期回調方法
     
        }
     
        private void init(){
     
            SurfaceHolder holder = getHolder();
            holder.addCallback(this); //設置Surface生命周期回調
            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) {
            thread.isRunning = false;
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
     
        /**
         * 執行繪制的繪制線程
         * @author Administrator
         *
         */
        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.YELLOW);
                paint.setStyle(Paint.Style.STROKE);
            }
     
            @Override
            public void run() {
     
                Canvas c = null;
     
                while(isRunning){
     
                    try{
                        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){
     
                //這個很重要,清屏操作,清楚掉上次繪制的殘留圖像
                c.drawColor(Color.BLACK);
     
                c.translate(200, 200);
                c.drawCircle(0,0, radius++, paint);
     
                if(radius > 100){
                    radius = 10f;
                }
     
            }
     
        }
     
    }

    上面代碼編寫了一個使用SurfaceView制作的動畫效果,它的效果跟上面自定義View的一樣,但是這邊的SurfaceView可以控制動 畫的幀數。在SurfaceView中內置一個LoopThread線程,這個線程的作用就是用來繪制圖形,在SurfaceView中實例化一個 LoopThread實例,一般這個操作會放在SurfaceView的構造方法中。然后通過在SurfaceView中的SurfaceHolder的 生命周期回調方法中插入一些操作,當Surface被創建時(SurfaceView顯示在屏幕中時),開啟LoopThread執行繪 制,LoopThread會一直刷新SurfaceView對象,當SurfaceView被隱藏時就停止改線程釋放資源。這邊有幾個地方要注意下:

    1.因為SurfaceView允許自定義的線程操作Surface對象執行繪制方法,而你可能同時定義多個線程執行繪制,所以當你獲取 SurfaceHolder中的Canvas對象時記得加同步操作,避免兩個不同的線程同時操作同一個Canvas對象,當操作完成后記得調用 SurfaceHolder.unlockCanvasAndPost方法釋放掉Canvas鎖。

    2.在調用doDraw執行繪制時,因為SurfaceView的特點,它會保留之前繪制的圖形,所以你需要先清空掉上一次繪制時留下的圖形。(View則不會,它默認在調用View.onDraw方法時就自動清空掉視圖里的東西)。

    3. 記得在回調方法:onSurfaceDestroyed方法里將后臺執行繪制的LoopThread關閉,這里是使用join方法。這涉及到線程如何關閉 的問題,多數人建議是通過一個標志位:isRunning來判斷線程是否該停止運行,如果你想關閉線程只需要將isRunning改成false即可,線 程會自動執行完run方法后退出。

     

    總結:

    通過上面的分析,現在大家應該會簡單使用SurfaceView了,總的歸納起來SurfaceView和View不同之處有:

    1. SurfaceView允許其他線程更新視圖對象(執行繪制方法)而View不允許這么做,它只允許UI線程更新視圖對象。

    2. SurfaceView是放在其他最底層的視圖層次中,所有其他視圖層都在它上面,所以在它之上可以添加一些層,而且它不能是透明的。

    3. 它執行動畫的效率比View高,而且你可以控制幀數。

    4. 因為它的定義和使用比View復雜,占用的資源也比較多,除非使用View不能完成,再用SurfaceView否則最好用View就可以。(貪吃蛇,俄羅斯方塊,棋牌類這種幀數比較低的可以使用View做就好)

    主站蜘蛛池模板: 亚洲熟妇无码一区二区三区 | 亚洲AV无码XXX麻豆艾秋| 1000部禁片黄的免费看| 亚洲AV综合色区无码一区爱AV | av无码东京热亚洲男人的天堂| 亚洲精品欧美综合四区| 午夜成年女人毛片免费观看| 亚洲熟妇无码av另类vr影视 | 少妇高潮太爽了在线观看免费| 亚洲一区二区三区免费视频| 野花高清在线电影观看免费视频| 亚洲在成人网在线看| 大学生高清一级毛片免费| 久久亚洲精品无码gv| 亚洲国产小视频精品久久久三级| 一区二区三区免费视频播放器| 国产V亚洲V天堂A无码| 97在线视频免费公开观看| 亚洲国产精品成人精品小说| 天天摸天天操免费播放小视频| 羞羞视频免费观看| 国产亚洲自拍一区| 99re热精品视频国产免费| 97久久国产亚洲精品超碰热| 国产又粗又长又硬免费视频| jizz中国免费| 亚洲日本中文字幕| 四虎永久在线精品免费网址 | 黄网站色视频免费在线观看的a站最新| 亚洲国产天堂久久综合网站| 91免费资源网站入口| 男人j进女人p免费视频| 国产AV无码专区亚洲精品| 国产麻豆视频免费观看| 狠狠热精品免费观看| 亚洲一区影音先锋色资源| 国内自产拍自a免费毛片| 国产午夜精品理论片免费观看 | 久久久久久久国产免费看 | h视频在线观看免费网站| 国产精品无码亚洲精品2021|