Android中SurfaceView和普通view的區(qū)別及使用
1 SurfaceView介紹
SurfaceView第一印象它是一個(gè)view,因?yàn)樗^承了View,有兩個(gè)直接子類GLSurfaceView,VideoView。但根據(jù)SDK文檔SurfaceView和普通的view又有較大區(qū)別。
最顯著的區(qū)別就是普通view和它的宿主窗口共享一個(gè)繪圖表面(Surface),SurfaceView雖然也在View的樹(shù)形結(jié)構(gòu)中,但是它有屬于自己的繪圖表面,Surface 內(nèi)部持有一個(gè)Canvas,可以利用這個(gè)Canvas繪制。
SurfaceView提供一個(gè)直接的繪圖表面(Surface)嵌入到視圖結(jié)構(gòu)層次中。你可以控制這個(gè)Surface的格式,大小,SurfaceView負(fù)責(zé)在屏幕上正確的擺放Surface。簡(jiǎn)單說(shuō)就是SurfaceView擁有自己的Surface,它與宿主窗口是分離的。
我們知道窗口中的view共享一個(gè)window,window又對(duì)應(yīng)一個(gè)Surface,所以窗口中的view共享一個(gè)Surface,而SurfaceView擁有自己的Surface。SurfaceView會(huì)創(chuàng)建一個(gè)置于應(yīng)用窗口之后的新窗口,SurfaceView相當(dāng)于在Window上挖一個(gè)洞,它就是顯示在這個(gè)洞里,其他的View是顯示在Window上,所以View可以顯示在 SurfaceView之上,也可以添加一些層在SurfaceView之上。
SurfaceView的窗口刷新的時(shí)候不需要重繪應(yīng)用程序的窗口而android普通窗口的視圖繪制機(jī)制是一層一層的,任何一個(gè)子元素或者是局部的刷新都會(huì)導(dǎo)致整個(gè)視圖結(jié)構(gòu)全部重繪一次。
對(duì)于普通的view,Android中的窗口界面包括多個(gè)View組成的View Hierachy的樹(shù)形結(jié)構(gòu),只有最頂層的DecorView才對(duì)WMS可見(jiàn),這個(gè)DecorView在WMS中有一個(gè)對(duì)應(yīng)的WindowState,此時(shí)APP請(qǐng)求創(chuàng)建Surface時(shí),會(huì)在SurfaceFlinger內(nèi)部建立對(duì)應(yīng)的Layer。而對(duì)于SurfaceView它自帶一個(gè)Surface,這個(gè)Surface在WMS有自己對(duì)應(yīng)的WindowState,在SurfaceFlinger中有自己對(duì)應(yīng)的layer。SurfaceView從APP端看它仍然在View hierachy結(jié)構(gòu)中,但在WMS和SurfaceFlinger中它與宿主窗口是分離的。因此SurfaceView的Surface的渲染可以放到單獨(dú)線程去做,不會(huì)影響主線程對(duì)事件的響應(yīng)。但因?yàn)檫@個(gè)Surface不在View hierachy中,它的顯示也不受View的屬性控制,所以不能進(jìn)行平移,縮放等變換(對(duì)SurfaceView進(jìn)行ScrollBy,ScrollTo操作沒(méi)有效果(還有透明度,旋轉(zhuǎn)),普通View進(jìn)行平移操作,內(nèi)部?jī)?nèi)容會(huì)移動(dòng),SurfaceView進(jìn)行這些操作內(nèi)部不會(huì)移動(dòng)。如果對(duì)包裹它的ViewGroup進(jìn)行平移旋轉(zhuǎn)等操作,也無(wú)法達(dá)到我們想要的效果。同時(shí)SurfaceView不能放在類似RecyclerView或ScrollView中,一些View中的特性也無(wú)法使用。
SurfaceView不支持平移,縮放,旋轉(zhuǎn)等動(dòng)畫(huà),但是當(dāng)我們利用SurfaceView測(cè)試這些不支持的動(dòng)畫(huà)時(shí),如果使用的是7.0 甚至更高版本的Android系統(tǒng),會(huì)發(fā)現(xiàn)SurfaceView也支持平移,縮放的動(dòng)畫(huà)操作。
View和SurfaceView的區(qū)別:
View適用主動(dòng)更新,SurfaceView 適用被動(dòng)更新,如頻繁的刷新
View在UI線程更新,在非UI線程更新會(huì)報(bào)錯(cuò),當(dāng)在主線程更新view時(shí)如果耗時(shí)過(guò)長(zhǎng)也會(huì)出錯(cuò), SurfaceView在子線程刷新不會(huì)阻塞主線程,適用于界面頻繁更新、對(duì)幀率要求較高的情況。
SurfaceView可以控制刷新頻率。
SurfaceView底層利用雙緩存機(jī)制,繪圖時(shí)不會(huì)出現(xiàn)閃爍問(wèn)題。
雙緩沖技術(shù)是游戲開(kāi)發(fā)中的一個(gè)重要的技術(shù),主要是為了解決 反復(fù)局部刷屏帶來(lái)的閃爍。游戲,視頻等畫(huà)面變化較頻繁,前面還沒(méi)有顯示完,程序又請(qǐng)求重新繪制,這樣屏幕就會(huì)不停地閃爍。雙緩沖技術(shù)會(huì)把要處理的圖片在內(nèi)存中處理好之后,把要畫(huà)的東西先畫(huà)到一個(gè)內(nèi)存區(qū)域里,然后整體的一次性畫(huà)出來(lái),將其顯示在屏幕上。
2 SurfaceView 使用步驟
使用SurfaceView的步驟:
首先要繼承SurfaceView,實(shí)現(xiàn)SurfaceHolder.Callback接口。
重寫(xiě)方法:
- surfaceChanged:surface大小或格式發(fā)生變化時(shí)觸發(fā),在surfaceCreated調(diào)用后該函數(shù)至少會(huì)被調(diào)用一次。
- surfaceCreated:Surface創(chuàng)建時(shí)觸發(fā),一般在這個(gè)函數(shù)開(kāi)啟繪圖線程(新的線程,不要再這個(gè)線程中繪制Surface)。
- surfaceDestroyed:銷毀時(shí)觸發(fā),一般不可見(jiàn)時(shí)就會(huì)銷毀。
利用getHolder()獲取SurfaceHolder對(duì)象,調(diào)用SurfaceHolder.addCallback添加回調(diào)
SurfaceHolder.lockCanvas 獲取Canvas對(duì)象并鎖定畫(huà)布,調(diào)用Canvas繪圖,SurfaceHolder.unlockCanvasAndPost 結(jié)束鎖定畫(huà)布,提交改變。
3 SurfaceHolder
SurfaceView的雙緩沖的機(jī)制非常消耗系統(tǒng)內(nèi)存,Android規(guī)定SurfaceView不可見(jiàn)時(shí),會(huì)立即銷毀SurfaceView的SurfaceHolder,以達(dá)到節(jié)約系統(tǒng)資源的目的,所以需要利用SurfaceHolder的回調(diào)函數(shù)對(duì)SurfaceHolder進(jìn)行維護(hù)。
提供了三個(gè)回調(diào)函數(shù)讓我們知道SurfaceHolder的創(chuàng)建、銷毀或者改變
void surfaceDestroyed(SurfaceHolder holder):當(dāng)SurfaceHolder被銷毀的時(shí)候回調(diào)。
void surfaceCreated(SurfaceHolder holder):當(dāng)SurfaceHolder被創(chuàng)建的時(shí)候回調(diào)。
void surfaceChange(SurfaceHolder holder):當(dāng)SurfaceHolder的尺寸或格式發(fā)生變化的時(shí)候被回調(diào)。
abstract Canvas lockCanvas():
獲取一個(gè)Canvas對(duì)象,并鎖定,得到的Canvas對(duì)象,就是Surface中一個(gè)成員。
** abstract Canvas lockCanvas(Rectdirty):**
僅僅鎖定dirty所指定的矩形區(qū)域。
abstract void unlockCanvasAndPost(Canvascanvas)
當(dāng)改動(dòng)Surface中的數(shù)據(jù)后,釋放同步鎖,并提交改變,然后將新的數(shù)據(jù)進(jìn)行展示,同一時(shí)候Surface中相關(guān)數(shù)據(jù)會(huì)被丟失。
public abstract void setType (int type):
設(shè)置Surface的類型,高版本中,setType這種方法已經(jīng)被depreciated了,系統(tǒng)會(huì)自動(dòng)設(shè)置。
- SURFACE_TYPE_NORMAL:用RAM緩存原生數(shù)據(jù)的普通Surface
- SURFACE_TYPE_HARDWARE:適用于DMA(Direct memory access )引擎和硬件加速的
- SurfaceSURFACE_TYPE_GPU:適用于GPU加速的Surface
- SURFACE_TYPE_PUSH_BUFFERS:表明該Surface不包括原生數(shù)據(jù),Surface用到的數(shù)據(jù)由其它對(duì)象提供,在Camera圖像預(yù)覽中就使用該類型的Surface,有Camera負(fù)責(zé)提供給預(yù)覽Surface數(shù)據(jù),生成圖像更流暢。
兼容性:
SurfaceView的兼容性
Android4.0以下SurfaceView不會(huì)自動(dòng)維護(hù)緩沖區(qū),播放視頻時(shí),如果使用SurfaceView開(kāi)發(fā)游戲應(yīng)用,就需要我們自己維護(hù)這個(gè)緩沖區(qū)了。
// 4.0版本之下需要設(shè)置的屬性 getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
4 SurfaceView的簡(jiǎn)單使用
繪制圓形:
public class SurfaceViewDemo extends SurfaceView implements SurfaceHolder.Callback{ private SurfaceHolder mSurfaceHolder; private Canvas mCanvas; private Paint paint; public SurfaceViewDemo(Context context) { this(context,null,0); } public SurfaceViewDemo(Context context, AttributeSet attrs) { this(context, attrs,0); } public SurfaceViewDemo(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(this); setFocusable(true); setFocusableInTouchMode(true); this.setKeepScreenOn(true); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.RED); paint.setStrokeWidth(5); paint.setStyle(Paint.Style.STROKE); } @Override public void surfaceCreated(SurfaceHolder holder) { System.out.println("=========surfaceCreated========"); new Thread(new Runnable() { @Override public void run() { draw(); } }).start(); } private void draw() { try { System.out.println("============draw========"); mCanvas = mSurfaceHolder.lockCanvas(); mCanvas.drawCircle(500,500,300,paint); mCanvas.drawCircle(100,100,20,paint); } catch (Exception e) { e.printStackTrace(); } finally { if (mCanvas != null) mSurfaceHolder.unlockCanvasAndPost(mCanvas); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { System.out.println("=========surfaceChanged========"); } @Override public void surfaceDestroyed(SurfaceHolder holder) { System.out.println("=========surfaceDestroyed========"); } }
回調(diào)函數(shù)的調(diào)用:
首次進(jìn)入:
=surfaceCreated
=surfaceChanged
====draw
點(diǎn)擊Home鍵:
=surfaceDestroyed
再次回到原來(lái)頁(yè)面:
=surfaceCreated
=surfaceChanged
====draw
所以當(dāng)SurfaceView不可見(jiàn)時(shí)會(huì)銷毀SurfaceHolder,再次進(jìn)入會(huì)重新調(diào)用surfaceCreated生成新的SurfaceHolder。surfaceChanged函數(shù)在surfaceCreated調(diào)用后該函數(shù)至少會(huì)被調(diào)用一次。
SurfaceView播放視頻,不要忘記存儲(chǔ)權(quán)限
mediaPlayer.setDisplay(getHolder());
視頻資源為利用模擬器錄制的mp4視頻
public class SurfaceViewDemo2 extends SurfaceView implements SurfaceHolder.Callback{ private SurfaceHolder mSurfaceHolder; private Canvas mCanvas; private Paint paint; private MediaPlayer mediaPlayer; public SurfaceViewDemo2(Context context) { this(context,null,0); } public SurfaceViewDemo2(Context context, AttributeSet attrs) { this(context, attrs,0); } public SurfaceViewDemo2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(this); setFocusable(true); setFocusableInTouchMode(true); this.setKeepScreenOn(true); setZOrderOnTop(true); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.RED); paint.setStrokeWidth(5); paint.setStyle(Paint.Style.STROKE); } @Override public void surfaceCreated(SurfaceHolder holder) { System.out.println("=========surfaceCreated========"); new Thread(new Runnable() { @Override public void run() { //draw(); play(); } }).start(); } private void draw() { try { System.out.println("============draw========"); mCanvas = mSurfaceHolder.lockCanvas(); mCanvas.drawCircle(500,500,300,paint); mCanvas.drawCircle(100,100,20,paint); } catch (Exception e) { e.printStackTrace(); } finally { if (mCanvas != null) mSurfaceHolder.unlockCanvasAndPost(mCanvas); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { System.out.println("=========surfaceChanged========"); } @Override public void surfaceDestroyed(SurfaceHolder holder) { System.out.println("=========surfaceDestroyed========"); if (mediaPlayer != null ){ stop(); } } protected void stop() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } } protected void play() { String path = "/sdcard/DCIM/Camera/VID_20190110_102218.mp4"; File file = new File(path); if (!file.exists()) { return; } try { mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 設(shè)置播放的視頻源 mediaPlayer.setDataSource(file.getAbsolutePath()); // 設(shè)置顯示視頻的SurfaceHolder mediaPlayer.setDisplay(getHolder()); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mediaPlayer.start(); } }); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { replay(); } }); mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { play(); return false; } }); } catch (Exception e) { e.printStackTrace(); } } protected void replay() { if (mediaPlayer!=null){ mediaPlayer.start(); }else{ play(); } } protected void pause() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); }else{ mediaPlayer.start(); } } }
補(bǔ)充 對(duì)SurfaceView進(jìn)行平移,旋轉(zhuǎn)等操作
添加 mSurfaceView.scrollBy(10,10);
效果如下:完全沒(méi)有效果
對(duì)包裹它的viewgroup添加縮放動(dòng)畫(huà)。
mContainer.animate().scaleX(0.4f).scaleY(0.7f);
此時(shí)拍攝出來(lái)的照片為:
拍攝出來(lái)的照片完全沒(méi)有受縮放的影響。
到此這篇關(guān)于Android中SurfaceView和普通view的區(qū)別及使用的文章就介紹到這了,更多相關(guān)Android中SurfaceView和普通view內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android中使用TextureView播放視頻
- Android中TextureView與SurfaceView用法區(qū)別總結(jié)
- Android使用MediaPlayer和TextureView實(shí)現(xiàn)視頻無(wú)縫切換
- Android OpenGL入門(mén)之GLSurfaceView
- Android SurfaceView基礎(chǔ)用法詳解
- Android 2d游戲開(kāi)發(fā)之貪吃蛇基于surfaceview
- Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的區(qū)別
- Android?中TextureView和SurfaceView的屬性方法及示例說(shuō)明
相關(guān)文章
基于GridView和ActivityGroup實(shí)現(xiàn)的TAB分頁(yè)(附源碼)
今天為大家介紹下使用GridView和ActivityGroup實(shí)現(xiàn)的分頁(yè),這里需要將Activity轉(zhuǎn)換成Window,然后再換成成View添加到容器中,具體實(shí)現(xiàn)代碼如下,感興趣的朋友可以參考下哈2013-06-06為Android系統(tǒng)添加config.xml 新配置的設(shè)置
這篇文章主要介紹了為Android系統(tǒng)添加config.xml 新配置的設(shè)置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03Android編程實(shí)現(xiàn)對(duì)電池狀態(tài)的監(jiān)視功能示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)對(duì)電池狀態(tài)的監(jiān)視功能,涉及Android基于廣播實(shí)現(xiàn)針對(duì)電源電量的判定與監(jiān)視技巧,需要的朋友可以參考下2016-11-11android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁(yè)效果實(shí)例代碼
本篇文章主要介紹了android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁(yè)效果實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03解決Android7.0更新后無(wú)法安裝的問(wèn)題
項(xiàng)目中發(fā)現(xiàn)在自動(dòng)更新功能的時(shí)候,下載好了apk的文件后在android7.0系統(tǒng)中不能自動(dòng)跳到安裝界面,后來(lái)搜索了一番解決了問(wèn)題,但感覺(jué)沒(méi)有描述清楚,所以補(bǔ)充一下。2017-12-12Android?獲取手機(jī)已安裝的應(yīng)用列表實(shí)現(xiàn)詳解
這篇文章主要介紹了Android?獲取手機(jī)已安裝的應(yīng)用列表的實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Android自定義控件實(shí)現(xiàn)球賽比分條效果
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)球賽比分條效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12在Android中調(diào)用WebService實(shí)例
這篇文章主要介紹了在Android中調(diào)用WebService實(shí)例,有需要的朋友可以了解一下。2016-11-11Android實(shí)現(xiàn)調(diào)用攝像頭進(jìn)行拍照功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)調(diào)用攝像頭進(jìn)行拍照功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04