1. 程式人生 > >Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之間的關係

Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之間的關係

一、Surface

Surface就是“表面”的意思。在SDK的文件中,對Surface的描述是這樣的:“Handle onto a raw buffer that is being managed by the screen compositor”,翻譯成中文就是“由螢幕顯示內容合成器(screen compositor)所管理的原生緩衝器的控制代碼”,這句話包括下面兩個意思:

1.      通過Surface(因為Surface是控制代碼)就可以獲得原生緩衝器以及其中的內容。就像在C語言中,可以通過一個檔案的控制代碼,就可以獲得檔案的內容一樣;

2.      原生緩衝器(rawbuffer)是用於儲存當前視窗的畫素資料的。

引伸地,可以認為Android中的Surface就是一個用來畫圖形(graphics)或影象(image)的地方。根據Java方面的常規知識,我們知道通常畫圖是在一個Canvas物件上面進行的,由此,可以推知一個Surface物件中應該包含有一個Canvas物件,事實上的確如此,而且這一點可以很容易通過debug執行程式的方式得到證明(將游標停留在物件變數surface上,會彈出一個對話方塊,其中紅色方框的內容,就表面surface中有一個CompatileCanvas成員變數)當然,看原始碼也是可以證明這一點:


因此,在前面提及的兩個意思的基礎上,可以再加上一條:

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

所以,Surface中的Canvas成員,是專門用於供程式設計師畫圖的場所,就像黑板一樣;其中的原生緩衝器是用來儲存資料的地方;Surface本身的作用類似一個控制代碼,得到了這個控制代碼就可以得到其中的Canvas、原生緩衝器以及其它方面的內容。

二、SurfaceView

SurfaceView,顧名思義就是Surface的View,通過SurfaceView就可以看到Surface的部分或者全部的內容,下面用一個圖來形象地描述一下Surface和SurfaceView的關係:


也就是說,Surface是用通過SurfaceView才能展示其中的內容。從這個意思上來說,SurfaceView中的View之確切的含義應該是viewport即“視口”的意思,做過資料庫設計的朋友知道,假定一個數據表有20個欄位,但我們常常只用到其中的5個欄位,那麼就可以在原資料表的基礎上,通過SQL語句CREATEVIEW來建立只包含那5個欄位內容的view。

另一方面,SurfaceView是Android中View的子類。事實上,在Android中所有用於介面展示的類皆為View的子類,包括那些不可見的、各種各樣的Layout。

所以說,SurfaceView中的View有兩個含義:

1.      視口(viewport)的意思

2.      SurfaceView是View的派生類

在Android中Surface是從Object派生而來,且實現了Parcelable介面。看到Parcelable就讓人能很自然地想到資料容器,SurfaceView就是用來展示Surface中的資料的。在這個層面上而言,Surface就是管理資料的地方,SurfaceView就是展示資料的地方。

三、SurfaceHolder

SurfaceHolder是一個介面,其作用就像一個關於Surface的監聽器。提供訪問和控制SurfaceView背後的Surface 相關的方法 (providingaccess and control over this SurfaceView's underlying surface),它通過三個回撥方法,讓我們可以感知到Surface的建立、銷燬或者改變。在SurfaceView中有一個方法getHolder,可以很方便地獲得SurfaceView所對應的Surface所對應的SurfaceHolder(有點拗口吧)。

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

為SurfaceHolder新增一個SurfaceHolder.Callback回撥介面。

獲取一個Canvas物件,並鎖定之。所得到的Canvas物件,其實就是Surface中一個成員。

同上。但只鎖定dirty所指定的矩形區域,因此效率更高。

當修改Surface中的資料完成後,釋放同步鎖,並提交改變,然後將新的資料進行展示,同時Surface中相關資料會被丟失。

5.      public abstract void setType (int type)

         設定Surface的型別,接收如下的引數:

SURFACE_TYPE_NORMAL:用RAM快取原生資料的普通Surface

SURFACE_TYPE_HARDWARE:適用於DMA(Direct memory access )引擎和硬體加速的Surface

SURFACE_TYPE_GPU:適用於GPU加速的Surface

SURFACE_TYPE_PUSH_BUFFERS:表明該Surface不包含原生資料,Surface用到的資料由其他物件提供,在Camera影象預覽中就使用該型別的Surface,有Camera負責提供給預覽Surface資料,這樣影象預覽會比較流暢。如果設定這種型別則就不能呼叫lockCanvas來獲取Canvas物件了。需要注意的是,在高版本的Android SDK中,setType這個方法已經被depreciated了。

2、3、4中的同步鎖機制的目的,就是為了在繪製的過程中,Surface中的資料不會被改變。

從設計模式的高度來看,Surface、SurfaceView和SurfaceHolder實質上就是廣為人知的MVC,即Model-View-Controller。Model就是模型的意思,或者說是資料模型,或者更簡單地說就是資料,也就是這裡的Surface;View即檢視,代表使用者互動介面,也就是這裡的SurfaceView;SurfaceHolder很明顯可以理解為MVC中的Controller(控制器)。這樣看起來三者之間的關係就清楚了很多。

四、SurfaceHolder.Callback

前面已經講到SurfaceHolder是一個介面,它通過回到方法的方式,讓我們可以感知到Surface的建立、銷燬或者改變。其實這一點是通過其內部的靜態子介面SurfaceHolder.Callback來實現的。SurfaceHolder.Callback中定義了三個介面方法:

1.     abstract void surfaceChanged(SurfaceHolderholder, int format, int width, int height)

當surface發生任何結構性的變化時(格式或者大小),該方法就會被立即呼叫。

當surface物件建立後,該方法就會被立即呼叫。

當surface物件在將要銷燬前,該方法會被立即呼叫。

在Android SDK文件中,關於SurfaceView的描述裡面,有一段這樣的話:

One of the purposes of this class is to provide a surface in which a secondarythread can render into the screen. If you are going to use it this way, youneed to be aware of some threading semantics:

-        All SurfaceView and SurfaceHolder.Callbackmethods will be called from the thread running the SurfaceView's window(typically the main thread of the application). They thus need to correctlysynchronize with any state that is also touched by the drawing thread.

-      You must ensure that the drawingthread only touches the underlying Surface while it is valid -- betweenSurfaceHolder.Callback.surfaceCreated() andSurfaceHolder.Callback.surfaceDestroyed().

這段話很重要,大致意思如下:

這個類的目的之一,就是提供一個可以用另外一個執行緒(第二個執行緒)進行螢幕渲染的surface(譯註:即UI執行緒和繪製執行緒可以分離)。如果你打算這樣使用,那麼應當注意一些執行緒方面的語義:

-           所有SurfaceView和SurfaceHolder.Callback中宣告的方法,必須在執行SurfaceView視窗中的執行緒中呼叫(典型地,就是應用的主執行緒。譯註:即UI執行緒),因為它們需要正確地將同時被繪製執行緒訪問的各種狀態進行同步。

-           必須保證,只有在背後的Surface有效的時候 – 在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()這兩個方法呼叫之間,訪問它。

1.        在Eclipse中建立一個Android Project專案TestSurfaceView,並選擇生成預設的Activity TestSurfaceViewActivity

2.        建立一個繪製執行緒如下:

  1. <span style="font-size:11px;">  
  2. package com.pat.testsurfaceview;  
  3. import android.graphics.Canvas;  
  4. import android.graphics.Color;  
  5. import android.graphics.Paint;  
  6. import android.graphics.Rect;  
  7. importandroid.view.SurfaceHolder;  
  8. // 繪製執行緒
  9. publicclass MyThread extendsThread  
  10. {  
  11.          private SurfaceHolder holder;  
  12.          privateboolean run;  
  13.          public MyThread(SurfaceHolder holder)  
  14.          {  
  15.                    this.holder = holder;  
  16.                    run = true;  
  17.          }  
  18.          @Override
  19.          publicvoid run()  
  20.          {  
  21.                    int counter = 0;  
  22.                    Canvas canvas = null;  
  23.                    while(run)  
  24.                    {  
  25.                             // 具體繪製工作
  26.                             try
  27.                             {  
  28.                                      // 獲取Canvas物件,並鎖定之
  29.                                      canvas= holder.lockCanvas();  
  30.                                      // 設定Canvas物件的背景顏色
  31.                                      canvas.drawColor(Color.WHITE);  
  32.                                      // 建立畫筆
  33.                                      Paintp = new Paint();  
  34.                                      // 設定畫筆顏色
  35.                                      p.setColor(Color.BLACK);  
  36.                                      // 設定文字大小
  37.                                      p.setTextSize(30);  
  38.                                      // 建立一個Rect物件rect
  39.                                      Rect rect = new Rect(10050380330);  
  40.                                      // 在canvas上繪製rect
  41.                                      canvas.drawRect(rect,p);  
  42.                                      // 在canvas上顯示時間
  43.                                      canvas.drawText("Interval = " + (counter++) + " seconds."100410, p);  
  44.                                      Thread.sleep(1000);  
  45.                             }  
  46.                             catch(Exception e)  
  47.                             {  
  48.                                      e.printStackTrace();  
  49.                             }  
  50.                             finally
  51.                             {  
  52.                                      if(canvas != null)  
  53.                                      {  
  54.                                                // 解除鎖定,並提交修改內容
  55.                                                holder.unlockCanvasAndPost(canvas);  
  56.                                      }  
  57.                             }  
  58.                    }  
  59.          }  
  60.          publicboolean isRun()  
  61.          {  
  62.                    return run;  
  63.          }  
  64.          publicvoid setRun(boolean run)  
  65.          {  
  66.                    this.run = run;  
  67.          }  
  68. }</span>  

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

  1. <span style="font-size:11px;">  
  2. package com.pat.testsurfaceview;  
  3. import android.content.Context;  
  4. import android.view.SurfaceHolder;  
  5. import android.view.SurfaceView;  
  6. publicclass MySurfaceView extends SurfaceView  
  7. implements
  8. SurfaceHolder.Callback  
  9. {  
  10.          private SurfaceHolder holder;  
  11.          private MyThread myThread;  
  12.          publicMySurfaceView(Context context)  
  13.          {  
  14.                    super(context);  
  15.                    // 通過SurfaceView獲得SurfaceHolder物件
  16.                    holder = getHolder();  
  17.                    // 為holder添加回調結構SurfaceHolder.Callback
  18.                    holder.addCallback(this);  
  19.                    // 建立一個繪製執行緒,將holder物件作為引數傳入,這樣在繪製執行緒中就可以獲得holder
  20.                    // 物件,進而在繪製執行緒中可以通過holder物件獲得Canvas物件,並在Canvas上進行繪製
  21.                    myThread = new MyThread(holder);  
  22.          }  
  23.          // 實現SurfaceHolder.Callback介面中的三個方法,都是在主執行緒中呼叫,而不是在繪製執行緒中呼叫的
  24.          @Override
  25.          publicvoid surfaceChanged(SurfaceHolder holder, int format, int width, int height)  
  26.          {  
  27.          }  
  28.          @Override
  29.          publicvoid surfaceCreated(SurfaceHolder holder)  
  30.          {  
  31.                    // 啟動執行緒。當這個方法呼叫時,說明Surface已經有效了
  32.                    myThread.setRun(true);  
  33.                    myThread.start();  
  34.          }  
  35.          @Override
  36.          publicvoid surfaceDestroyed(SurfaceHolderholder)  
  37.          {  
  38.                    // 結束執行緒。當這個方法呼叫時,說明Surface即將要被銷燬了
  39.                    myThread.setRun(false);  
  40.          }  
  41. }</span>  

4.    修改TestSurfaceViewActivity.java程式碼,使之如下:

  1. <span style="font-size:11px;">  
  2. package com.pat.testsurfaceview;  
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. publicclass TestSurfaceViewActivity extends Activity  
  6. 相關推薦

    Android SurfaceSurfaceViewSurfaceHolderSurfaceHolder.Callback之間關係

    最近學習Google的zxing掃碼,接觸到Surface、Surface和Viewfindview,這裡先記下Surface學習到的好東西 轉載:http://blog.csdn.net/pathuang68/article/details/7351317 一、Surface Surf

    Android圖形系統之SurfaceSurfaceViewSurfaceHolderSurfaceHolder.Callback之間的聯絡

    SurfaceHolder 結構 繼承關係 public interface SurfaceHolder android.view.SurfaceHolder  概述 抽象介面持有人顯示錶面。允許您控制面的大小和格式

    SurfaceSurfaceViewSurfaceHolderSurfaceHolder.Callback之間關係

    一、Surface Surface就是“表面”的意思。在SDK的文件中,對Surface的描述是這樣的:“Handle onto a raw buffer that is being managed by the screen compositor”,翻譯成中文就是“

    初學者對ServletJSPWeb容器之間關係的理解

    Servlet就是一個Java程式,一個Servlet應用程式經常包含一個或者多個Servlet也就是當用戶傳送請求,例如提交表單,那麼需要一個或多個Servlet協同作用,而訪問的jsp頁面也是一個S

    【OpenCV學習筆記】三十輪廓特徵屬性應用(七)—位置關係輪廓匹配

    輪廓特徵屬性及應用(七)—位置關係及輪廓匹配 1.計算點與輪廓的距離及位置關係——pointPolygonTest() 2.矩的計算——moments() 3.形狀匹配(比較兩個形狀或輪廓間的相似度)

    Android之——SurfaceSurfaceViewSurfaceHolder.Callback初探

    一、Surface    Surface在SDK的文件中的描述是這樣的:Handle onto a raw buffer that is being managed by the screen compositor,Android中的Surface就是一個用來畫圖形(grap

    My97設置開始結束 時間區間輸入框不能輸入只能選擇的方法

    開始時間 don min put 命名 class 方法 不能 設置 時間區間開始: <input type="text" id = "first_time" name="first_time" value="${first_time }" onFocus = "

    Linux存儲管理硬盤分區格式化掛載

    硬盤接口 mknod fdisk 下面介紹的是Linux的存儲管理基礎知識、使用一個新的硬盤設備步驟為(分區、格式化、掛載分區)。一、Linux的存儲管理基礎知識點1、硬件組成計算機基礎知識中,各個硬件表示為:運算器(CUP);控制器(MEEM);硬盤(HDisk);網卡(NIC,即net int

    IntelliJ Pycharmwebstorm 2017 註冊碼註冊服務器

    fhe 出現 usr wnlb 沒有 bag bbc 應該 targe jetbrains 家的東西都非常好看,但是價格貴的令人發指,所以我搭建了一個 Pycharm激活服務器,可以用來激活 Pycharm,IntelliJ IDEA,WebStorm。避免頻繁更換激活碼的

    HTML5音頻播放,歌詞同步,視頻播放功能(JPlayerJWPlayerVideoJS)

    cover swf ddl iis enter [0 fast absolute idt 近期項目中用到音頻視頻播放。所以就寫了一個demo: 這個是JPlayer插件的視頻播放: 這個是音頻播放,歌詞同步: <!DOCTYPE htm

    MapSetList集合差別聯系詳解

    特性 互轉 字母順序 時也 參數 很慢 未定義 諸多 cto 提到集合之前,先說說數組Array和集合的區別:   (1)數組是大小固定的,並且同一個數組只能存放類型一樣的數據(基本類型/引用類型)   (2)JAVA集合可以存儲和操作數目不固定的一組數據。    (

    python jsonmysql——讀取json文件存sql數據庫日期類型轉換終端操縱mysqlpython codecs讀取大文件問題

    temp extra log urn xtra mysql 程序 pre 安裝mysql preface: 近期幫師兄處理json文件,須要讀到數據庫裏面,以備其興許從數據庫讀取數據。數據是關於yelp站點裏面的: https://github.com/Yelp/d

    EurekaRibbonFeign常見問題解決

    16px 註冊 second value seconds list tor images ble 1、Eureka常見問 1.1、Eureka Enviroment 的配置 eureka.enviroment=product   參考 https://github.co

    46求1+2+3+...+n,要求不能使用乘除法forwhileifelseswitchcase等關鍵字條件判斷語句(A?B:C)。

    closed else while spl 判斷語句 stat 條件 執行 ret 思路:循環或者遞歸都有個結束條件和執行條件。用&&短路與代替。 //短路與&&;就是只有前一個條件滿足才可以去判斷第二個條件。 //遞歸的出口

    python操作excel網絡編程和異常處理

    size let finally 必須 新的 異常信息 屬性 開發 tar 一、python操作excel 1、讀excel,xlrd模塊用來讀excel # book = xlrd.open_workbook(r‘students.xlsx‘)#打開excel# prin

    【dubbo基礎】dubbo學習過程使用經驗分享實現原理簡單介紹

    multi spring配置 不同 影響 為什麽 exception 同事 sock services 一、前言 部門去年年中開始各種改造,第一步是模塊服務化,這邊初選dubbo試用在一些非重要模塊上,慢慢引入到一些稍微重要的功能上,半年時間,學習過程及線上使用遇到的些問

    sessionStoragelocalStorage 存儲如何存儲數組與對象

    return parse div func code 瀏覽器 typeof urn log 1、存儲,獲取,清楚 sessionStorage.setItem("key",val) sessionStorage.getItem("key") sessionStorage.

    視圖序列索引的創建用戶權限

    當前 下一個 rem incr user ssi from 系列 sta select * from scott.emp;--創建視圖create view waa as select * from scott.emp;drop view waa;select * from

    Delphi 的字符字符串 stringAnsiStringWideStringString[n]ShortString

    ssa 寬字符串 set setlength 內存 如果 指定大小 spa 字符 //最常用的 string var str: string; {定義} begin str := ‘萬一‘; {賦值} ShowMessage(IntToStr(Length(

    沫沫金原創提供:完整的根據身份證獲取省份性別年齡生日頁面驗證

    數字 部分 ast script cit key oot test log 概述: 身份證的校驗,識別,分離,處處可見。最近H5移動端的項目就需要掃碼獲取身份證,根據身份證自動識別省份、性別、年齡、生日信息。這裏分享完善版,希望大家喜歡。 環境: 依賴jQuery、Boot