SurfaceView淺析

什麼是SurfaceView?Surface的意思是表層,表面的意思,那麼SurfaceView就是指一個在表層的View對象。爲何 說是在表層呢,這是由於它有點特殊跟其餘View不同,其餘View是繪製在表層外,而它就是充當表層對象。假設你要在一個球上畫畫,那麼球的表層就當 作你的畫布對象,你畫的東西會擋住它的表層,咱們默認沒使用SurfaceView,那麼球的表層就是空白的,若是咱們使用了SurfaceView,我 們能夠理解爲咱們拿來的球自己表面就具備紋路,你是畫再紋路之上的,若是你畫的是半透明的,那麼你將能夠透過你畫的東西看到球面自己的紋路。SDK的文檔 說到:SurfaceView就是在Window上挖一個洞,它就是顯示在這個洞裏,其餘的View是顯示在Window上,因此View能夠顯式在 SurfaceView之上,你也能夠添加一些層在SurfaceView之上。php

SurfaceView還有其餘的特性,上面咱們講了它能夠控制幀數,那它是什麼控制的呢?這就須要瞭解它的使用機制。通常在不少遊戲設計中,咱們都是開闢一個後臺線程計算遊戲相關的數據,而後根據這些計算完的新數據再刷新視圖對象,因爲對View執行繪製操做只能在UI線程上, 因此當你在另一個線程計算完數據後,你須要調用View.invalidate方法通知系統刷新View對象,因此遊戲相關的數據也須要讓UI線程能訪 問到,這樣的設計架構比較複雜,要是能讓後臺計算的線程能直接訪問數據,而後更新View對象那改多好。咱們知道View的更新只能在UI線程中,因此使 用自定義View沒辦法這麼作,可是SurfaceView就能夠了。它一個很好用的地方就是容許其餘線程(不是UI線程)繪製圖形(使用Canvas),根據它這個特性,你就能夠控制它的幀數,你若是讓這個線程1秒執行50次繪製,那麼最後顯示的就是50幀。html

 

如何使用SurfaceView?

首先SurfaceView也是一個View,它也有本身的生命週期。由於它須要另一個線程來執行繪製操做,因此咱們能夠在它生命週期的初始化階 段開闢一個新線程,而後開始執行繪製,當生命週期的結束階段咱們插入結束繪製線程的操做。這些是由其內部一個SurfaceHolder對象完成的。 SurfaceHolder,顧名思義,它裏面保存了一個隊Surface對象的引用,而咱們執行繪製方法就是操做這個 Surface,SurfaceHolder由於保存了對Surface的引用,因此使用它來處理Surface的生命週期,說到底 SurfaceView的生命週期其實就是Surface的生命週期,由於SurfaceHolder保存對Surface的引用,因此使用 SurfaceHolder來處理生命週期的初始化。android

 

SurfaceView是視圖(View)的繼承類,這個視圖裏內嵌了一個專門用於繪製的Surface。你能夠控制這個Surface的格式和尺寸。Surfaceview控制這個Surface的繪製位置。
        surface是縱深排序(Z-ordered)的,這代表它總在本身所在窗口的後面。surfaceview提供了一個可見區域,只有在這個可見區域內 的surface部份內容纔可見,可見區域外的部分不可見。surface的排版顯示受到視圖層級關係的影響,它的兄弟視圖結點會在頂端顯示。這意味者 surface的內容會被它的兄弟視圖遮擋,這一特性能夠用來放置遮蓋物(overlays)(例如,文本和按鈕等控件)。注意,若是surface上面 有透明控件,那麼它的每次變化都會引發框架從新計算它和頂層控件的透明效果,這會影響性能。
        你能夠經過SurfaceHolder接口訪問這個surface,getHolder()方法能夠獲得這個接口。
        surfaceview變得可見時,surface被建立;surfaceview隱藏前,surface被銷燬。這樣能節省資源。若是你要查看 surface被建立和銷燬的時機,能夠重載surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
        surfaceview的核心在於提供了兩個線程:UI線程和渲染線程。這裏應注意:
        1> 全部SurfaceView和SurfaceHolder.Callback的方法都應該在UI線程裏調用,通常來講就是應用程序主線程。渲染線程所要訪問的各類變量應該做同步處理。
        2> 因爲surface可能被銷燬,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之間有效,因此要確保渲染線程訪問的是合法有效的surface。編程

 

接下來呢,說說本身對它的理解
一、定義canvas

能夠直接從內存或者DMA等硬件接口取得圖像數據,是個很是重要的繪圖容器。架構

它的特性是:能夠在主線程以外的線程中向屏幕繪圖上。這樣能夠避免畫圖任務繁重的時候形成主線程阻塞,從而提升了程序的反應速度。在遊戲開發中多用到SurfaceView,遊戲中的背景、人物、動畫等等儘可能在畫布canvas中畫出。app

二、實現框架

首先繼承SurfaceView並實現SurfaceHolder.Callback接口
使用接口的緣由:由於使用SurfaceView 有一個原則,全部的繪圖工做必須得在Surface 被建立以後才能開始(Surface—表面,這個概念在 圖形編程中經常被提到。基本上咱們能夠把它看成顯存的一個映射,寫入到Surface 的內容
                      能夠被直接複製到顯存從而顯示出來,這使得顯示速度會很是快),而在Surface 被銷燬以前必須結束。因此Callback 中的surfaceCreated 和surfaceDestroyed 就成了繪圖處理代碼的邊界。ide

須要重寫的方法函數

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

     //在surface的大小發生改變時激發

 (2)public void surfaceCreated(SurfaceHolder holder){}

     //在建立時激發,通常在這裏調用畫圖的線程。

 (3)public void surfaceDestroyed(SurfaceHolder holder) {}

     //銷燬時激發,通常在這裏將畫圖的線程中止、釋放。

整個過程:繼承SurfaceView並實現SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()得到SurfaceHolder對象 ---->SurfaceHolder.addCallback(callback)添加回調函數---->SurfaceHolder.lockCanvas()得到Canvas對象並鎖定畫布----> Canvas繪畫 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)結束鎖定畫圖,並提交改變,將圖形顯示。


三、SurfaceHolder
這裏用到了一個類SurfaceHolder,能夠把它當成surface的控制器,用來操縱surface。處理它的Canvas上畫的效果和動畫,控制表面,大小,像素等。
幾個須要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 給SurfaceView當前的持有者一個回調對象。
(2)、abstract Canvas lockCanvas();
// 鎖定畫布,通常在鎖定後就能夠經過其返回的畫布對象Canvas,在其上面畫圖等操做了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 鎖定畫布的某個區域進行畫圖等..由於畫完圖後,會調用下面的unlockCanvasAndPost來改變顯示內容。
// 相對部份內存要求比較高的遊戲來講,能夠不用重畫dirty外的其它區域的像素,能夠提升速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 結束鎖定畫圖,並提交改變。
四、實例

這裏的例子實現了一個矩形和一個計時器

 

package xl.test;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class ViewTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this));
    }
    //視圖內部類
    class MyView extends SurfaceView implements SurfaceHolder.Callback
    {
        private SurfaceHolder holder;
        private MyThread myThread; 
        public MyView(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
            holder = this.getHolder();
            holder.addCallback(this);
            myThread = new MyThread(holder);//建立一個繪圖線程
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
            // TODO Auto-generated method stub
            
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            myThread.isRun = true;
            myThread.start();
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            myThread.isRun = false;
        }
        
    }
    //線程內部類
    class MyThread extends Thread
    {
        private SurfaceHolder holder;
        public boolean isRun ;
        public  MyThread(SurfaceHolder holder)
        {
            this.holder =holder; 
            isRun = true;
        }
        @Override
        public void run()
        {
            int count = 0;
            while(isRun)
            {
                Canvas c = null;
                try
                {
                    synchronized (holder)
                    {
                        c = holder.lockCanvas();//鎖定畫布,通常在鎖定後就能夠經過其返回的畫布對象Canvas,在其上面畫圖等操做了。
                        c.drawColor(Color.BLACK);//設置畫布背景顏色
                        Paint p = new Paint(); //建立畫筆
                        p.setColor(Color.WHITE);
                        Rect r = new Rect(100, 50, 300, 250);
                        c.drawRect(r, p);
                        c.drawText("這是第"+(count++)+"秒", 100, 310, p);
                        Thread.sleep(1000);//睡眠時間爲1秒
                    }
                }
                catch (Exception e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                finally
                {
                    if(c!= null)
                    {
                        holder.unlockCanvasAndPost(c);//結束鎖定畫圖,並提交改變。

                    }
                }
            }
        }
    }
}

 

結來講,就是SurfaceView有如下三個特色:

 

       A. 具備獨立的繪圖表面;

        B. 須要在宿主窗口上挖一個洞來顯示本身;

        C. 它的UI繪製能夠在獨立的線程中進行,這樣就能夠進行復雜的UI繪製,而且不會影響應用程序的主線程響應用戶輸入。

什麼是雙緩衝?

 

不用畫布,直接在窗口上進行繪圖叫作無緩衝繪圖。用了一個畫布,將全部內容都先畫到畫布上,在總體繪製到窗口上,就該叫作單緩衝繪圖,那個畫布就是一個緩衝區。用了兩個畫布,一個進行臨時的繪圖,一個進行最終的繪圖,這樣就叫作雙緩衝繪圖。

surfaceView自身實現了雙緩衝,而View沒有。其實view你也能夠本身實現,可是實現的結構不如surfaceView好。

    surfaceView經過 surfaceHolder.lockCanvas 鎖定畫布,實現下一張圖片的繪製,再經過另外的線程刷新界面,繪製圖片。

    view則是直接在ondraw裏繪製圖片,刷新界面。其實view也能夠實現雙緩衝機制,你能夠在另個出ondraw的方法中繪製下一張bitmap(參見:http://blog.csdn.net/liubingzhao/article/details/5563113),也能夠另開一個線程,處理除了繪製圖片之外的操做(參見:http://topic.csdn.net/u/20110901/23/e283f805-20dc-40c3-8381-403dd1ca69b0.html),就實現了view的雙緩衝。

爲何動態繪圖surfaceView要比View好?

由於View是在UI主線程中進行繪製的,繪製時會阻塞主線程,若是ontouch事件又處理的比較多的話會致使界面卡。而surfaceView是另開了一個線程繪製的,再加上雙緩衝機制,因此要高效。不會卡。其實如今通常實現view的時候通常都會在其餘出先生成bitmap在給ondraw去畫,因此雙緩衝的做用不是那麼明顯了。

相關文章
相關標籤/搜索