標籤:des android style blog http io color ar os
轉載請包括網址:http://blog.csdn.net/pathuang68/article/details/7351317
一、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對象,其實的確如此,並且這一點能夠非常easy通過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還提供了非常多重要的方法,當中最重要的就是:
1. abstract void addCallback(SurfaceHolder.Callbackcallback)
為SurfaceHolder加入?一個SurfaceHolder.Callback回調介面。
2. abstract Canvas lockCanvas()
擷取一個Canvas對象,並鎖定之。所得到的Canvas對象,事實上就是Surface中一個成員。
3. abstract Canvas lockCanvas(Rectdirty)
同上。但僅僅鎖定dirty所指定的矩形地區,因此效率更高。
4. abstract void unlockCanvasAndPost(Canvascanvas)
當改動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發生不論什麼結構性的變化時(格式或者大小),該方法就會被馬上調用。
2. abstract void surfaceCreated(SurfaceHolderholder)
當surface對象建立後,該方法就會被馬上調用。
3. abstract void surfaceDestroyed(SurfaceHolderholder)
當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()這兩個方法調用之間,訪問它。
以下,我們通過一個很easy範例來實際感受一下(代碼摘自http://www.cnblogs.com/xuling/archive/2011/06/06/android.html,並做了一些結構性的修改),請留意代碼中的凝視:
1. 在Eclipse中建立一個Android Project項目TestSurfaceView,並選擇產生預設的Activity TestSurfaceViewActivity
2. 建立一個繪製線程例如以下:
package com.pat.testsurfaceview; import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;importandroid.view.SurfaceHolder; // 繪製線程public class MyThread extendsThread{ 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); // 建立畫筆 Paintp = 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類例如以下:
package com.pat.testsurfaceview; import android.content.Context;import android.view.SurfaceHolder;import android.view.SurfaceView; public class MySurfaceView extends SurfaceViewimplementsSurfaceHolder.Callback{ private SurfaceHolder holder; private MyThread myThread; publicMySurfaceView(Context context) { super(context); // 通過SurfaceView獲得SurfaceHolder對象 holder = getHolder(); // 為holder加入?回調結構SurfaceHolder.Callback holder.addCallback(this); // 建立一個繪製線程,將holder對象作為參數傳入,這樣在繪製線程中就能夠獲得holder // 對象,進而在繪製線程中能夠通過holder對象獲得Canvas對象,並在Canvas上進行繪製 myThread = new MyThread(holder); } // 實現SurfaceHolder.Callback介面中的三個方法,都是在主線程中調用,而不是在繪製線程中調用的 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { // 啟動線程。當這種方法調用時,說明Surface已經有效了 myThread.setRun(true); myThread.start(); } @Override public void surfaceDestroyed(SurfaceHolderholder) { // 結束線程。當這種方法調用時,說明Surface即將要被銷毀了 myThread.setRun(false); }}
4. 改動TestSurfaceViewActivity.java代碼,使之例如以下:
package com.pat.testsurfaceview; import android.app.Activity;import android.os.Bundle; public class TestSurfaceViewActivity extends Activity{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.main); setContentView(new MySurfaceView(this)); }}
執行結果:
非常顯然,我們能夠在MyThread的run方法中,做非常多更有意思的事情。弄清楚了Surface、SurfaceView、SurfaceHolder和SurfaceHolder.Callback這些概念,以及它們之間的關係,對我們更好地使用它們應該會有相當大的協助。
Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之間的關係