轉載請包括網址: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 SurfaceView
implements
SurfaceHolder.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這些概念,以及它們之間的關系,對我們更好地使用它們應該會有相當大的幫助。