Android中的Surface和SurfaceView


一、什么是Surface

           簡單的說Surface對應了一塊屏幕緩沖區,每個window對應一個Surface,任何View都要畫在Surface的Canvas上(后面有原因解釋)。傳統的view共享一塊屏幕緩沖區,所有的繪制必須在UI線程中進行。

        在SDK的文檔中,對Surface的描述是這樣的:“Handle onto a raw buffer that is being managed by the screen compositor”,翻譯成中文就是“由屏幕顯示內容合成器(screen compositor)所管理的原始緩沖區的句柄”,這句話包括下面兩個意思:

        1、通過Surface(因為Surface是句柄)就可以獲得原生緩沖器以及其中的內容。就像在C++語言中,可以通過一個文件的句柄,就可以獲得文件的內容一樣。

        2、 原始緩沖區(a raw buffer)是用於保存當前窗口的像素數據的。

        引伸地,可以認為Android中的Surface就是一個用來畫圖形(graphics)或圖像(image)的地方。

        根據Java方面的常規知識,我們知道通常畫圖是在一個Canvas對象上面進行的,由此,可以推知一個Surface對象中應該包含有一個Canvas(畫布)對象。因此,在前面提及的兩個意思的基礎上,可以再加上一條:

        3、Surface中有一個Canvas成員,專門用於畫圖的。

        由以上的概括,我們總結如下:Surface中的Canvas成員,是專門用於供程序員畫圖的場所,就像黑板一樣;其中的原始緩沖區是用來保存數據的地方;Surface本身的作用類似一個句柄,得到了這個句柄就可以得到其中的Canvas、原始緩沖區以及其它方面的內容。

      Surface是用來管理數據的。(句柄)

二、什么是SurfaceView

         說SurfaceView是一個View也許不夠嚴謹,然而從定義中pubilc classSurfaceView extends View{.....}顯示SurfaceView確實是派生自View,但是SurfaceView卻有自己的Surface,請看SurfaceView的源碼:

  1. <span style="font-size:14px;color:#333333;">   if (mWindow == null) {    
             mWindow = new MyWindow(this);    
             mLayout.type = mWindowType;    
             mLayout.gravity = Gravity.LEFT|Gravity.TOP;    
             mSession.addWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,    
             mVisible ? VISIBLE : GONE, mContentInsets);     
       }  </span>  

     

        很明顯,每個SurfaceView創建的時候都會創建一個MyWindow,new MyWindow(this)中的this正是SurfaceView自身,因此將SurfaceView和window綁定在一起,由第一部分我們知道,一個window對應一個Surface,因此SurfaceView也就內嵌了一個自己的Surface,可以認為SurfaceView是用來控制Surface中View的位置和尺寸的。

 

         SurfaceView就是展示Surface中數據的地方,同時可以認為SurfaceView是用來控制Surface中View的位置和尺寸的。

        大家都知道,傳統View及其派生類的更新只能在UI線程,然而UI線程還同時處理其他交互邏輯,這就無法保證View更新的速度和幀率了,而SurfaceView可以用獨立的線程進行繪制,因此可以提供更高的幀率,例如游戲,攝像頭取景等場景就比較適合SurfaceView來實現。

 

三、什么是SurfaceHolder

        SurfaceHolder是一個接口,其作用就像一個關於Surface的監聽器,提供訪問和控制SurfaceView內嵌的Surface 相關的方法。它通過三個回調方法,讓我們可以感知到Surface的創建、銷毀或者改變。

        在SurfaceView中有一個方法getHolder,可以很方便地獲得SurfaceView內嵌的Surface所對應的監聽器接口SurfaceHolder(有點拗口吧)。

        除下面將要提到的SurfaceHolder.Callback外,SurfaceHolder還提供了很多重要的方法,其中最重要的就是:

       1、abstract void addCallback(SurfaceHolder.Callbackcallback):為SurfaceHolder添加一個SurfaceHolder.Callback回調接口。

       2、abstract Canvas lockCanvas():獲取一個Canvas對象,並鎖定之。所得到的Canvas對象,其實就是Surface中一個成員。

       3、abstract Canvas lockCanvas(Rect  dirty):同上。但只鎖定dirty所指定的矩形區域,因此效率更高。

       4、abstract void unlockCanvasAndPost(Canvas  canvas):當修改Surface中的數據完成后,釋放同步鎖,並提交改變,然后將新的數據進行展示,同時Surface中相關數據會被丟失。

        2、3、4中的同步鎖機制的目的,就是為了在繪制的過程中,Surface中的數據不會被改變。lockCanvas是為了防止同一時刻多個線程對同一canvas寫入。

總結:從設計模式的高度來看,Surface、SurfaceView和SurfaceHolder實質上就是廣為人知的MVC,即Model-View-Controller。Model就是模型的意思,或者說是數據模型,或者更簡單地說就是數據,也就是這里的Surface;View即視圖,代表用戶交互界面,也就是這里的SurfaceView;SurfaceHolder很明顯可以理解為MVC中的Controller(控制器)。

四、什么是SurfaceHolder.Callback

        SurfaceHolder.Callback主要是當底層的Surface被創建、銷毀或者改變時提供回調通知,由於繪制必須在Surface被創建后才能進行,因此SurfaceHolder.Callback中的surfaceCreated 和surfaceDestroyed 就成了繪圖處理代碼的邊界。

        SurfaceHolder.Callback中定義了三個接口方法:

        1、abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height):當surface發生任何結構性的變化時(格式或者大小),該方法就會被立即調用。

        2、abstract void surfaceCreated(SurfaceHolder holder):當surface對象創建后,該方法就會被立即調用。

        3、abstract void  surfaceDestroyed(SurfaceHolder holder):當surface對象在將要銷毀前,該方法會被立即調用。

 

五、實例演示

        下面,我們通過一個非常簡單例子來實際感受一下,請留意代碼中的注釋:

        1、在Eclipse中創建一個Android Project項目TestSurfaceView,並選擇生成缺省的Activity TestSurfaceViewActivity

        2、創建一個繪制線程如下: 

 
  1. import android.graphics.Canvas;  
    import android.graphics.Color;  
    import android.graphics.Paint;  
    import android.graphics.Rect;  
    importandroid.view.SurfaceHolder;  
      
    // 繪制線程  
    public class MyThread extends Thread  
    {  
             private SurfaceHolder holder;  
             private boolean run;  
              
             public  MyThread(SurfaceHolder holder)  
             {  
                       this.holder = holder;  
                       run = true;  
             }  
      
             @Override  
             public void run()  
             {  
                       int counter = 0;  
                       Canvas canvas = null;  
                       while(run)  
                       {  
                                // 具體繪制工作  
                                try  
                                {  
                                         // 獲取Canvas對象,並鎖定之  
                                         canvas= holder.lockCanvas();  
                                          
                                         // 設定Canvas對象的背景顏色  
                                         canvas.drawColor(Color.WHITE);  
                                          
                                         // 創建畫筆  
                                         Paint p = new Paint();  
                                         // 設置畫筆顏色  
                                         p.setColor(Color.BLACK);  
                                         // 設置文字大小  
                                         p.setTextSize(30);  
                                          
                                         // 創建一個Rect對象rect  
                                         Rect  rect = new Rect(100, 50, 380, 330);  
                                         // 在canvas上繪制rect  
                                         canvas.drawRect(rect,p);  
                                         // 在canvas上顯示時間  
                                         canvas.drawText("Interval = " + (counter++) + " seconds.", 100, 410, p);  
                                         Thread.sleep(1000);  
                                }  
                                catch(Exception e)  
                                {  
                                         e.printStackTrace();  
                                }  
                                finally  
                                {  
                                         if(canvas != null)  
                                         {  
                                                   // 解除鎖定,並提交修改內容  
                                                   holder.unlockCanvasAndPost(canvas);  
                                         }  
                                }  
                       }  
             }  
      
             public boolean isRun()  
             {  
                       return run;  
             }  
              
             public void setRun(boolean run)  
             {  
                       this.run = run;  
             }  
    }  

     

 

       3、自定義一個SurfaceView類如下: 

 
  1.  1 import android.content.Context;  
     2 import android.view.SurfaceHolder;  
     3 import android.view.SurfaceView;  
     4   
     5 public class MySurfaceView extends SurfaceView<span style="font-family:Arial;"> </span>implements<span style="font-family:Arial;">  </span>SurfaceHolder.Callback  
     6 {  
     7          private SurfaceHolder holder;  
     8          private MyThread myThread;  
     9           
    10          public  MySurfaceView(Context context)  
    11          {  
    12                    super(context);  
    13                     
    14                    // 通過SurfaceView獲得SurfaceHolder對象  
    15                    holder = getHolder();  
    16                     
    17                    // 為holder添加回調結構SurfaceHolder.Callback  
    18                    holder.addCallback(this);  
    19                     
    20                    // 創建一個繪制線程,將holder對象作為參數傳入,這樣在繪制線程中就可以獲得holder  
    21                    // 對象,進而在繪制線程中可以通過holder對象獲得Canvas對象,並在Canvas上進行繪制  
    22                    myThread = new MyThread(holder);  
    23          }  
    24   
    25          // 實現SurfaceHolder.Callback接口中的三個方法,都是在主線程中調用,而不是在繪制線程中調用的  
    26          @Override  
    27          public void  surfaceChanged(SurfaceHolder holder, int format, int width, int height)  
    28          {  
    29          }  
    30   
    31          @Override  
    32          public void  surfaceCreated(SurfaceHolder holder)  
    33          {  
    34                    // 啟動線程。當這個方法調用時,說明Surface已經有效了  
    35                    myThread.setRun(true);  
    36                    myThread.start();  
    37          }  
    38   
    39          @Override  
    40          public void surfaceDestroyed(SurfaceHolder holder)  
    41          {  
    42                    // 結束線程。當這個方法調用時,說明Surface即將要被銷毀了  
    43                    myThread.setRun(false);  
    44          }  
    45 }  

     


        4、修改TestSurfaceViewActivity.java代碼,使之如下:

 

      1import android.app.Activity;  

  1.  2 import android.os.Bundle;  
     3   
     4 public class TestSurfaceViewActivity extends Activity  
     5 {  
     6     @Override  
     7     public void onCreate(Bundle savedInstanceState)  
     8     {  
     9         super.onCreate(savedInstanceState);  
    10         //setContentView(R.layout.main);  
    11         setContentView(new MySurfaceView(this));  
    12     }  
    13 }  

     


運行結果:

 

 

原文:http://blog.csdn.net/tgww88/article/details/7973476

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM