參考:http://blog.csdn.net/heikefangxian23/article/details/50110007
首先我們先來看下官方API對SurfaceView的介紹
SurfaceView的API介紹
Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, >its size; the SurfaceView takes care of placing the surface at the correct location on the screen
The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.
Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling getHolder().
The Surface will be created for you while the SurfaceView's window is visible; you should implement surfaceCreated(SurfaceHolder) and surfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed as the window is shown and hidden.
One of the purposes of this class is to provide a surface in which a secondary thread can render in to the screen. If you are going to use it this way, you need to be aware of some threading semantics:
?All SurfaceView and SurfaceHolder.Callback methods will be called from the thread running the SurfaceView's window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched by the drawing thread.
?You must ensure that the drawing thread only touches the underlying Surface while it is valid -- between SurfaceHolder.Callback.surfaceCreated() and SurfaceHolder.Callback.surfaceDestroyed().
對應(yīng)的中文翻譯
SurfaceView是視圖(View)的繼承類,這個視圖里內(nèi)嵌了一個專門用于繪制的Surface。你可以控制這個Surface的格式和尺寸。Surfaceview控制這個Surface的繪制位置。
surface是縱深排序(Z-ordered)的,這表明它總在自己所在窗口的后面。surfaceview提供了一個可見區(qū)域,只有在這個可見區(qū)域內(nèi) 的surface部分內(nèi)容才可見,可見區(qū)域外的部分不可見。surface的排版顯示受到視圖層級關(guān)系的影響,它的兄弟視圖結(jié)點會在頂端顯示。這意味者 surface的內(nèi)容會被它的兄弟視圖遮擋,這一特性可以用來放置遮蓋物(overlays)(例如,文本和按鈕等控件)。注意,如果surface上面 有透明控件,那么它的每次變化都會引起框架重新計算它和頂層控件的透明效果,這會影響性能。
你可以通過SurfaceHolder接口訪問這個surface,getHolder()方法可以得到這個接口。
surfaceview變得可見時,surface被創(chuàng)建;surfaceview隱藏前,surface被銷毀。這樣能節(jié)省資源。如果你要查看 surface被創(chuàng)建和銷毀的時機(jī),可以重載surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
surfaceview的核心在于提供了兩個線程:UI線程和渲染線程。這里應(yīng)注意:
1> 所有SurfaceView和SurfaceHolder.Callback的方法都應(yīng)該在UI線程里調(diào)用,一般來說就是應(yīng)用程序主線程。渲染線程所要訪問的各種變量應(yīng)該作同步處理。
2> 由于surface可能被銷毀,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之間有效,所以要確保渲染線程訪問的是合法有效的surface。
SurfaceView理解
什么是SurfaceView呢?
為什么是SurfaceView呢?Surface的意思是表層,表面的意思,那么SurfaceView就是指一個在表層的View對象。為什么 說是在表層呢,這是因為它有點特殊跟其他View不一樣,其他View是繪制在表層外,而它就是充當(dāng)表層對象。假設(shè)你要在一個球上畫畫,那么球的表層就當(dāng) 做你的畫布對象,你畫的東西會擋住它的表層,我們默認(rèn)沒使用SurfaceView,那么球的表層就是空白的,如果我們使用了SurfaceView,我 們可以理解為我們拿來的球本身表面就具有紋路,你是畫再紋路之上的,如果你畫的是半透明的,那么你將可以透過你畫的東西看到球面本身的紋路。SDK的文檔 說到:SurfaceView就是在Window上挖一個洞,它就是顯示在這個洞里,其他的View是顯示在Window上,所以View可以顯式在 SurfaceView之上,你也可以添加一些層在SurfaceView之上。
SurfaceView還有其他的特性,上面我們講了它可以控制幀數(shù),那它是什么控制的呢?這就需要了解它的使用機(jī)制。一般在很多游戲設(shè)計中,我們都是開辟一個后臺線程計算游戲相關(guān)的數(shù)據(jù),然后根據(jù)這些計算完的新數(shù)據(jù)再刷新視圖對象,由于對View執(zhí)行繪制操作只能在UI線程上, 所以當(dāng)你在另外一個線程計算完數(shù)據(jù)后,你需要調(diào)用View.invalidate方法通知系統(tǒng)刷新View對象,所以游戲相關(guān)的數(shù)據(jù)也需要讓UI線程能訪 問到,這樣的設(shè)計架構(gòu)比較復(fù)雜,要是能讓后臺計算的線程能直接訪問數(shù)據(jù),然后更新View對象那改多好。我們知道View的更新只能在UI線程中,所以使 用自定義View沒辦法這么做,但是SurfaceView就可以了。它一個很好用的地方就是允許其他線程(不是UI線程)繪制圖形(使用Canvas),根據(jù)它這個特性,你就可以控制它的幀數(shù),你如果讓這個線程1秒執(zhí)行50次繪制,那么最后顯示的就是50幀。
官話說完了,再來看一下具體的說明:
一.為什么使用SurfaceView
大多數(shù)情況下我們的自定義View都會選擇去繼承View或ViewGroup來實現(xiàn),但是為什么系統(tǒng)還要為我們提供一個SurfaceView呢?
首先我們知道View類如果需要更新視圖,必須我們主動的去調(diào)用invalidate()或者postInvalidate()方法來再走一次onDraw()完成更新。但是呢,Android系統(tǒng)規(guī)定屏幕的刷新間隔為16ms,如果這個View在16ms內(nèi)更新完畢了,就不會卡頓,但是如果邏輯操作太多,16ms內(nèi)沒有更新完畢,剩下的操作就會丟到下一個16ms里去完成,這樣就會造成UI線程的阻塞,造成View的運動過程掉幀,自然就會卡頓了。
所以這些原因也就促使了SurfaceView的存在。畢竟,如果是一個游戲,它有可能相當(dāng)頻繁的有更新畫面的需求。
二.SurfaceView的優(yōu)點:
1.SurfaceView的刷新處于主動,有利于頻繁的更新畫面。
2.SurfaceView的繪制在子線程進(jìn)行,避免了UI線程的阻塞。
3.SurfaceView在底層實現(xiàn)了一個雙緩沖機(jī)制,效率大大提升。
4 .SurfaceView可以自定義幀數(shù),例如讓這個線程1秒執(zhí)行50次繪制,那么最后顯示的就是50幀
三.SurfaceView的使用
public class AnimView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder = null; //控制對象
public AnimView(Context context, AttributeSet attr) {
super(context, attr);
holder = getHolder();
holder.addCallback(this);
this.setZOrderOnTop(true);
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
//當(dāng)SurfaceChange的時候
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
//Surface創(chuàng)建的時候調(diào)用 一般用于創(chuàng)建線程 執(zhí)行一些初始化
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
}
public synchronized void doDraw(int tt, int width, int height, Paint p, int argb1, int argb2) {
Canvas canvas = holder.lockCanvas();
if(canvas==null) {
return;
}
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
p.setColor(argb1);
canvas.drawCircle(width / 2, height, tt, p);
if (tt - 300 > 0) {
p.setColor(argb2);
canvas.drawCircle(width / 2, height, tt - 300, p);
}
holder.unlockCanvasAndPost(canvas);
}
public void startAnimation() {
try {
Thread t = new Thread(new MyLoop());
t.start();
t.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
return;
}
class MyLoop implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
int width = getWidth();
int height = getHeight();
Paint p = new Paint();
int argb1 = Color.argb(80, 250, 0, 0);
int argb2 = Color.argb(120, 250, 0, 0);
p.setAntiAlias(true); //反鋸齒
for (int i = 0; i < height + 500; i += 50) {
doDraw(i,width,height,p,argb1,argb2);
}
}
}
}
1.先看AnimView的構(gòu)造方法:
holder = getHolder(); //getHolder獲取SurfaceHolder對象用來添加 生命周期回調(diào)方法
holder.addCallback(this);//添加生命周期回調(diào)
this.setZOrderOnTop(true);//設(shè)置可以懸浮在其他View上面
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);//設(shè)置懸浮為透明,否則會遮蓋下面的View,例如背景之類的。
2.再看SurfaceView的三個生命周期方法
surfaceCreated(SurfaceHolder holder)//當(dāng)SurfaceView創(chuàng)建的時候,一般在surfaceCreated方法里開啟一個子線程。
surfaceChanged(SurfaceHolder holder, int format, int width, int height) //當(dāng)SurfaceView改變的時候
surfaceDestroyed(SurfaceHolder holder)//當(dāng)SurfaceView完成銷毀的時候,一般用于對線程進(jìn)行銷毀
上面代碼中沒有用到這三個周期,因為可能會在當(dāng)activity剛顯示的時候并不需要就開啟線程去開啟SurfaceView動畫,因此在上面提供了一個
startAnimation去處理線程的開啟。
上面我們說SurfaceView不影響主線程的運行去改變View,而我們做的這個動畫是當(dāng)SurfaceView執(zhí)行完之后再去執(zhí)行下一步操作,所以在StartAnimation中t.join();讓主線程阻塞起來。
3.在MyLoop任務(wù)中定義了一個for循環(huán)去畫兩個圓,分別定義兩個不同的透明度的顏色
4.注意看doDraw方法
1.Synchronized關(guān)鍵字
因為SurfaceView允許自定義的線程操作Surface對象執(zhí)行繪制方法,而你可能同時定義多個線程執(zhí)行繪制,所以當(dāng)你獲取 SurfaceHolder中的Canvas對象時記得加同步操作,避免兩個不同的線程同時操作同一個Canvas對象,當(dāng)操作完成后記得調(diào)用 SurfaceHolder.unlockCanvasAndPost方法釋放掉Canvas鎖。
Canvas canvas = holder.lockCanvas();//獲取鎖住的Canvas對象
holder.unlockCanvasAndPost(canvas);//操作完成釋放
2.**canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); **
在調(diào)用doDraw執(zhí)行繪制時,因為SurfaceView的特點,它會保留之前繪制的圖形,所以你需要先清空掉上一次繪制時留下的圖形。(View則不會,它默認(rèn)在調(diào)用View.onDraw方法時就自動清空掉視圖里的東西)。
最后看下xml布局
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:orientation="vertical">
<com.tencent.widget.AnimView
android:id="@+id/animview"
android:background="@mipmap/timg"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
最后效果


動態(tài)圖:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
就是從下面咻咻咻上去