在Android系統(tǒng)中,有一種特殊的視圖地淀,稱為SurfaceView嬉探。
什么場(chǎng)景選用SurfaceView
SurfaceView概念:
SurfaceView本身是一個(gè)View,符合一切View的特性排惨,需要通過(guò)Canvas畫布繪制。
developer官方文檔對(duì)SurfaceView的定義:
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.
The transparent region that makes the surface visible is based on the layout positions in the view hierarchy. If the post-layout transform properties are used to draw a sibling view on top of the SurfaceView, the view may not be properly composited with the surface.
總結(jié)幾個(gè)要點(diǎn):
- SurfaceView擁有獨(dú)立的Surface(繪圖表面)
- SurfaceView是用Zorder排序的碰凶,他默認(rèn)在宿主Window的后面暮芭,SurfaceView通過(guò)在Window上面“挖洞”(設(shè)置透明區(qū)域)進(jìn)行顯示
SurfaceView與View的區(qū)別
- View的繪圖效率不高鹿驼,主要用于動(dòng)畫變化較少的程序
- SurfaceView 繪圖效率較高,用于界面更新頻繁的程序
- SurfaceView擁有獨(dú)立的Surface(繪圖表面)辕宏,即它不與其宿主窗口共享同一個(gè)Surface畜晰。
一般來(lái)說(shuō),每一個(gè)窗口在SurfaceFlinger服務(wù)中都對(duì)應(yīng)有一個(gè)Layer瑞筐,用來(lái)描述它的繪圖表面凄鼻。對(duì)于那些具有SurfaceView的窗口來(lái)說(shuō),每一個(gè)SurfaceView在SurfaceFlinger服務(wù)中還對(duì)應(yīng)有一個(gè)獨(dú)立的Layer或者LayerBuffer聚假,用來(lái)單獨(dú)描述它的繪圖表面块蚌,以區(qū)別于它的宿主窗口的繪圖表面。
因此SurfaceView的UI就可以在一個(gè)獨(dú)立的線程中進(jìn)行繪制膘格,可以不會(huì)占用主線程資源匈子。 - SurfaceView使用雙緩沖機(jī)制,播放視頻時(shí)畫面更流暢
什么是雙緩沖機(jī)制
在運(yùn)用時(shí)可以理解為:SurfaceView在更新視圖時(shí)用到了兩張 Canvas闯袒,一張 frontCanvas 和一張 backCanvas 虎敦,每次實(shí)際顯示的是 frontCanvas ,backCanvas 存儲(chǔ)的是上一次更改前的視圖政敢。當(dāng)你在播放這一幀的時(shí)候其徙,它已經(jīng)提前幫你加載好后面一幀了,所以播放起視頻很流暢喷户。
當(dāng)使用lockCanvas()獲取畫布時(shí)唾那,得到的實(shí)際上是backCanvas 而不是正在顯示的 frontCanvas ,之后你在獲取到的 backCanvas 上繪制新視圖褪尝,再 unlockCanvasAndPost(canvas)此視圖闹获,那么上傳的這張 canvas 將替換原來(lái)的 frontCanvas 作為新的frontCanvas ,原來(lái)的 frontCanvas 將切換到后臺(tái)作為 backCanvas 河哑。例如避诽,如果你已經(jīng)先后兩次繪制了視圖A和B,那么你再調(diào)用 lockCanvas()獲取視圖璃谨,獲得的將是A而不是正在顯示的B沙庐,之后你將重繪的 A 視圖上傳,那么 A 將取代 B 作為新的 frontCanvas 顯示在SurfaceView 上佳吞,原來(lái)的B則轉(zhuǎn)換為backCanvas拱雏。
相當(dāng)與多個(gè)線程,交替解析和渲染每一幀視頻數(shù)據(jù)底扳。
使用場(chǎng)景
所以SurfaceView一方面可以實(shí)現(xiàn)復(fù)雜而高效的UI铸抑,另一方面又不會(huì)導(dǎo)致用戶輸入得不到及時(shí)響應(yīng)。常用于畫面內(nèi)容更新頻繁的場(chǎng)景衷模,比如游戲鹊汛、視頻播放和相機(jī)預(yù)覽菇爪。
使用SurfaceView的三步驟
如何使用SurfaceView呢?
1柒昏、獲取SurfaceHolder對(duì)象凳宙,其是SurfaceView的內(nèi)部類。添加回調(diào)監(jiān)聽Surface生命周期职祷。
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
2氏涩、surfaceCreated 回調(diào)后啟動(dòng)繪制線程
只有當(dāng)native層的Surface創(chuàng)建完畢之后,才可以調(diào)用lockCanvas()有梆,否則失敗是尖。
@Override
public void surfaceCreated(SurfaceHolder holder) {
mDrawThread = new DrawThread();
mDrawThread.start();
}
3、繪制
Canvas canvas = mSurfaceHolder.lockCanvas();
// 使用canvas繪制內(nèi)容
...
mSurfaceHolder.unlockCanvasAndPost(canvas);
使用SurfaceView不顯示問題
發(fā)生這種問題的原因是多層嵌套被遮擋
解決方法是根據(jù)具體情況調(diào)用如下api接口:
setZOrderOnTop(boolean onTop) // 在最頂層泥耀,會(huì)遮擋一切view
setZOrderMediaOverlay(boolean isMediaOverlay)// 如已繪制SurfaceView則在surfaceView上一層繪制饺汹。
看下他們的源碼:
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
mSubLayer = isMediaOverlay
? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
}
public void setZOrderOnTop(boolean onTop) {
if (onTop) {
mSubLayer = APPLICATION_PANEL_SUBLAYER;
} else {
mSubLayer = APPLICATION_MEDIA_SUBLAYER;
}
}
兩個(gè)方法都是給mSubLayer賦值,所以需要注意這兩個(gè)接口同時(shí)調(diào)用后一個(gè)會(huì)覆蓋前一個(gè)的效果痰催。
黑色背景問題
//設(shè)置背景透明
mHolder.setFormat(PixelFormat.TRANSPARENT);