Android開(kāi)發(fā)中的Surface庫(kù)及用其制作播放器UI的例子
1、Surface
1.1、 就如在C語(yǔ)言編程一樣,通過(guò)一個(gè)文件的句柄,就可以操作文件,獲取文件的內(nèi)容。 同樣的,通過(guò)Surface就可以獲取raw buffer其中的內(nèi)容。原生緩沖區(qū)(raw buffer)存儲(chǔ)著當(dāng)前窗口的像素?cái)?shù)據(jù)。
1.2、事實(shí)上,當(dāng)?shù)玫揭粋€(gè)Surface對(duì)象時(shí),同時(shí)會(huì)得到一個(gè)Canvas(畫(huà)布)對(duì)象。這一點(diǎn)可以通過(guò)查看\frameworks\base\core\java\android\view\Surface.java文件可知道Surface類(lèi)定義了一個(gè)Canvas成員變量
private int mSurfaceControl; private int mSaveCount; private Canvas mCanvas; private int mNativeSurface; private int mSurfaceGenerationId; private String mName;
1.3、 理解Canvas對(duì)象,可以把它當(dāng)做畫(huà)布,Canvas的方法大多數(shù)是設(shè)置畫(huà)布的大小、形狀、畫(huà)布背景顏色等等,要想在畫(huà)布上面畫(huà)畫(huà),一般要與Paint對(duì)象結(jié)合使用,顧名思義,Paint就是畫(huà)筆的風(fēng)格,顏料的色彩之類(lèi)的。
// 創(chuàng)建畫(huà)筆 Paint paint = new Paint(); paint.setColor(Color.RED);// 設(shè)置紅色 canvas.drawCircle(60, 20, 10, paint);// 畫(huà)一個(gè)圓
1.4、Surface本身的作用類(lèi)似一個(gè)句柄,得到了這個(gè)句柄就可以得到其中的Canvas、原生緩沖器以及其它方面的內(nèi)容。
1.5、Surface實(shí)現(xiàn)了Parcelable接口,(implements Parcelable),也就是說(shuō)Surface對(duì)象可以把顯示內(nèi)容的數(shù)據(jù)寫(xiě)入到 Parcel 中,并且能夠從Parcel讀回?cái)?shù)據(jù)。
2、SurfaceView
SurfaceView提供了一個(gè)專(zhuān)門(mén)用于繪制的surface,這個(gè)surface內(nèi)嵌于。你可以控制這個(gè)Surface的格式和尺寸。Surfaceview控制這個(gè)Surface在屏幕的正確繪制位置。
surface是Z-ordered的(也就是說(shuō)在xyz坐標(biāo)系中,按照Z(yǔ)坐標(biāo)排序的,Z值大的表面覆蓋在Z值小的表面的上方),這表明它總在自己所在窗口的后面。surfaceview在顯示窗口處為Surface提供了一個(gè)可見(jiàn)區(qū)域,通過(guò)這個(gè)區(qū)域,才能看到Surface里面的內(nèi)容??梢苑胖靡恍└采w圖層(overlays)在Surface上面,如Button、Textview之類(lèi)的。但是,需要注意的是,如果Surface上面有全透明的控件,那么隨著Surface的每一次變化,這些全透明的控件就會(huì)重新渲染,這樣的話(huà),就影響性能與顯示的效果。
你可以通過(guò)SurfaceHolder這個(gè)接口去訪(fǎng)問(wèn)Surface,而執(zhí)行g(shù)etHolder()方法可以得到SurfaceHolder接口。
當(dāng)SurfaceView的窗口可見(jiàn)時(shí),Surface就會(huì)被創(chuàng)建,當(dāng)SurfaceView窗口隱藏時(shí),Surface就會(huì)被銷(xiāo)毀。當(dāng)然了,你也可以通過(guò)復(fù)寫(xiě)surfaceCreated(SurfaceHolder) 和 surfaceDestroyed(SurfaceHolder) 這兩個(gè)方法來(lái)驗(yàn)證一下Surface何時(shí)被創(chuàng)建與何時(shí)被銷(xiāo)毀。
SurfaceView提供了一個(gè)運(yùn)行在渲染線(xiàn)程的surface,若你要更新屏幕,你需要了解以下線(xiàn)程知識(shí)。
所有SurfaceView 和 SurfaceHolder.Callback的方法都應(yīng)該在主線(xiàn)程(UI線(xiàn)程)里面調(diào)用,應(yīng)該要確保渲染進(jìn)程所訪(fǎng)問(wèn)變量的同步性。
你必須確保只有當(dāng)Surface有效的時(shí)候,(也就是當(dāng)Surface的生命周期在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之間)才能讓渲染進(jìn)程訪(fǎng)問(wèn)。
2.1、SurfaceView與Surface的聯(lián)系
簡(jiǎn)單來(lái)說(shuō),SurfaceView與Surface的聯(lián)系就是,Surface是管理顯示內(nèi)容的數(shù)據(jù)(implementsParcelable),包括存儲(chǔ)于數(shù)據(jù)的交換。而SurfaceView就是把這些數(shù)據(jù)顯示出來(lái)到屏幕上面。
兩者聯(lián)系如圖所示:
3、SurfaceHolder
SurfaceHolder是控制surface的一個(gè)抽象接口,你可以通過(guò)SurfaceHolder來(lái)控制surface的尺寸和格式,或者修改surface的像素,監(jiān)視surface的變化等等,SurfaceHolder是SurfaceView的典型接口。
與直接控制SurfaceView來(lái)修改surface不同,使用SurfaceHolder來(lái)修改surface時(shí),需要注意lockCanvas() 和Callback.surfaceCreated().這兩個(gè)方法。
SurfaceHolder控制surface的流程所使用的幾個(gè)方法。
3.1、abstract void addCallback(SurfaceHolder.Callback callback)
給SurfaceHolder一個(gè)回調(diào)對(duì)象。
3.2、abstract Canvas lockCanvas(Rect dirty)
鎖定畫(huà)布中的某一個(gè)區(qū)域,返回的畫(huà)布對(duì)象Canvas(當(dāng)更新的內(nèi)容只有一個(gè)區(qū)域時(shí),同時(shí)要追求高效,可以只更
新一部分的區(qū)域,而不必更新全部畫(huà)布區(qū)域)
3.3、abstract Canvas lockCanvas()
鎖定畫(huà)布,返回的畫(huà)布對(duì)象Canvas
3.4、abstract void removeCallback(SurfaceHolder.Callback callback)
移除回調(diào)對(duì)象
3.5、abstract void unlockCanvasAndPost(Canvas canvas)
結(jié)束鎖定畫(huà)圖,并提交改變。
4、SurfaceHolder.Callback
SurfaceHolder.Callback是監(jiān)聽(tīng)surface改變的一個(gè)接口
4.1、public abstract voidsurfaceChanged(SurfaceHolder holder, int format, int width, int height)
surface發(fā)生改變時(shí)被調(diào)用
4.2、public abstract voidsurfaceCreated(SurfaceHolder holder)
在surface創(chuàng)建時(shí)被調(diào)用,一般在這個(gè)方法里面開(kāi)啟渲染屏幕的線(xiàn)程。
4.3、public abstract voidsurfaceDestroyed(SurfaceHolder holder)
銷(xiāo)毀時(shí)被調(diào)用,一般在這個(gè)方法里將渲染的線(xiàn)程停止。
附上上述所說(shuō)幾種的聯(lián)系方法
SurfaceHolder = SurfaceView.getHolder(); Surface = SurfaceHolder.getSurface(); Canvas =SurfaceHolder.LockCanvas(Rect dirty) Canvas =Surface.lockCanvas(Rect dirty)
5、DEMO:通過(guò)SurfaceView以及SurfaceHolder進(jìn)行視頻播放
使用AudioView進(jìn)行視頻播放的時(shí)候,是不是很不爽,千篇一律的模式,惡心吧。這里,我們可以通過(guò)一些方式對(duì)MediaPlayer進(jìn)行包裝。而所用到的正是SurfaceView以及SurfaceHolder。
最終效果圖:
我們提供了四個(gè)按鈕,可以進(jìn)行播放控制。
布局文件media.xml代碼:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <SurfaceView android:id="@+id/surfaceView1" android:layout_width="320px" android:layout_height="160px"></SurfaceView> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageButton android:id="@+id/button_play" android:src="@drawable/play" android:onClick="buttonClick" android:layout_width="wrap_content" android:layout_height="wrap_content"></ImageButton> <ImageButton android:id="@+id/button_pause" android:src="@drawable/pause" android:onClick="buttonClick" android:layout_width="wrap_content" android:layout_height="wrap_content"></ImageButton> <ImageButton android:id="@+id/button_stop" android:src="@drawable/stop" android:onClick="buttonClick" android:layout_width="wrap_content" android:layout_height="wrap_content"></ImageButton> <ImageButton android:id="@+id/button_reset" android:src="@drawable/reset" android:onClick="buttonClick" android:layout_width="wrap_content" android:layout_height="wrap_content"></ImageButton> </LinearLayout> </LinearLayout>
activity代碼:
package cn.com.chenzheng_java.media; import android.app.Activity; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Bundle; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; /** * @description 通過(guò)SurfaceView/SurfaceHolder實(shí)現(xiàn)自己的播放器 * @author chenzheng_java * @since 2011/03/23 * @description 這里進(jìn)行一下補(bǔ)充說(shuō)明,我們可以通過(guò)mediaplayer添加OnPreparedListener * 以及OnCompletionListener等事件對(duì)準(zhǔn)備好播放以及播放完成后的操作進(jìn)行控制。 * 使用SurfaceView以及SurfaceHolder進(jìn)行視頻播放時(shí),結(jié)構(gòu)是這樣的: * 1、首先,我們從布局文件中獲取一個(gè)surfaceView * 2、通過(guò)surfaceView.getHolder()方法獲取與該容器想對(duì)應(yīng)的surfaceHolder * 3、對(duì)srufaceHolder進(jìn)行一些默認(rèn)的設(shè)置,如addCallback()和setType() * 4、通過(guò)mediaPlayer.setDisplay()方法將視頻播放與播放容器鏈接起來(lái) */ public class MyMediaPlayerActivity extends Activity { MediaPlayer mediaPlayer ; // 播放器的內(nèi)部實(shí)現(xiàn)是通過(guò)MediaPlayer SurfaceView surfaceView ;// 裝在視頻的容器 SurfaceHolder surfaceHolder;// 控制surfaceView的屬性(尺寸、格式等)對(duì)象 boolean isPause ; // 是否已經(jīng)暫停了 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.media); surfaceView = (SurfaceView) findViewById(R.id.surfaceView1); /** * 獲取與當(dāng)前surfaceView相關(guān)聯(lián)的那個(gè)的surefaceHolder */ surfaceHolder = surfaceView.getHolder(); /** * 注冊(cè)當(dāng)surfaceView創(chuàng)建、改變和銷(xiāo)毀時(shí)應(yīng)該執(zhí)行的方法 */ surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i("通知", "surfaceHolder被銷(xiāo)毀了"); if(mediaPlayer!=null) mediaPlayer.release(); } @Override public void surfaceCreated(SurfaceHolder holder) { Log.i("通知", "surfaceHolder被create了"); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i("通知", "surfaceHolder被改變了"); } }); /** * 這里必須設(shè)置為SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS哦,意思 * 是創(chuàng)建一個(gè)push的'surface',主要的特點(diǎn)就是不進(jìn)行緩沖 */ surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } /*** * @param targetButton 被用戶(hù)點(diǎn)擊的按鈕 */ public void buttonClick(View targetButton){ int buttonId = targetButton.getId(); switch (buttonId) { case R.id.button_play: play(); break; case R.id.button_pause: pause(); break; case R.id.button_reset: reset(); break; case R.id.button_stop: stop(); break; default: break; } } /** * 播放 */ private void play(){ mediaPlayer = new MediaPlayer(); // 設(shè)置多媒體流類(lèi)型 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 設(shè)置用于展示mediaPlayer的容器 mediaPlayer.setDisplay(surfaceHolder); try { mediaPlayer.setDataSource("/data/jinsha.3gp"); mediaPlayer.prepare(); mediaPlayer.start(); isPause = false; } catch (Exception e) { Log.i("通知", "播放過(guò)程中出現(xiàn)了錯(cuò)誤哦"); } } /** * 暫停 */ private void pause(){ Log.i("通知", "點(diǎn)擊了暫停按鈕"); if(isPause==false){ mediaPlayer.pause(); isPause=true; }else{ mediaPlayer.start(); isPause=false; } } /** * 重置 */ private void reset(){ Log.i("通知", "點(diǎn)擊了reset按鈕"); // 跳轉(zhuǎn)到視頻的最開(kāi)始 mediaPlayer.seekTo(0); mediaPlayer.start(); } /** * 停止 */ private void stop(){ Log.i("通知", "點(diǎn)擊了stop按鈕"); mediaPlayer.stop(); mediaPlayer.release(); } }
- android暫?;蛲V蛊渌魳?lè)播放器的播放實(shí)現(xiàn)代碼
- android webvie指定視頻播放器播放網(wǎng)站視頻
- 教你輕松制作Android音樂(lè)播放器
- Android自定義播放器控件VideoView
- Android編程開(kāi)發(fā)音樂(lè)播放器實(shí)例
- android音樂(lè)播放器監(jiān)聽(tīng)電話(huà)狀態(tài)實(shí)現(xiàn)代碼
- Android應(yīng)用開(kāi)發(fā)之簡(jiǎn)易、大氣音樂(lè)播放器實(shí)現(xiàn)專(zhuān)輯倒影效果
- Android App中使用AudioManager類(lèi)來(lái)編寫(xiě)音頻播放器
- Android繪制音樂(lè)播放器示波器
- Android基于Service的音樂(lè)播放器
相關(guān)文章
Android自定義View展示W(wǎng)ifi信號(hào)強(qiáng)弱指示方法示例
這篇文章主要給大家介紹了關(guān)于Android自定義View展示W(wǎng)ifi信號(hào)強(qiáng)弱指示的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),文末給出了完整的實(shí)例供大家參考學(xué)習(xí),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08Android WebView實(shí)現(xiàn)全屏播放視頻
WebView是Android系統(tǒng)中的原生控件,其主要功能與前端頁(yè)面進(jìn)行響應(yīng)交互,快捷省時(shí)地實(shí)現(xiàn)如期的功能,相當(dāng)于增強(qiáng)版的內(nèi)置瀏覽器。這篇文章主要介紹的是利用WebView實(shí)現(xiàn)全屏播放視頻的功能,感興趣的小伙伴可以了解一下2021-12-12Android 自定義View結(jié)合自定義TabLayout實(shí)現(xiàn)頂部標(biāo)簽滑動(dòng)效果
小編最近在做app的項(xiàng)目,需要用到tablayout實(shí)現(xiàn)頂部的滑動(dòng)效果,文中代碼用到了自定義item,代碼也很簡(jiǎn)單,感興趣的朋友跟隨腳本之家小編一起看看吧2018-07-07Android 自定義view和屬性動(dòng)畫(huà)實(shí)現(xiàn)充電進(jìn)度條效果
近期項(xiàng)目中需要使用到一種類(lèi)似手機(jī)電池充電進(jìn)度的動(dòng)畫(huà)效果,以前沒(méi)學(xué)屬性動(dòng)畫(huà)的時(shí)候,是用圖片+定時(shí)器的方式來(lái)完成的,下面給大家分享android自定義view和屬性動(dòng)畫(huà)實(shí)現(xiàn)充電進(jìn)度條2016-12-12從零開(kāi)始使用gradle配置即可執(zhí)行的Hook庫(kù)詳解
這篇文章主要為大家介紹了從零開(kāi)始使用gradle配置即可執(zhí)行的Hook庫(kù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Android注冊(cè)登錄實(shí)時(shí)自動(dòng)獲取短信驗(yàn)證碼
注冊(cè)登錄或修改密碼功能常常需要輸入短信驗(yàn)證碼,如何自動(dòng)獲取短信驗(yàn)證碼,這篇文章就為大家介紹了Androidcv注冊(cè)登錄自動(dòng)獲取短信驗(yàn)證碼的實(shí)現(xiàn)代碼,感興趣的小伙伴們可以參考一下2016-08-08Android編程之退出整個(gè)應(yīng)用程序的方法
這篇文章主要介紹了Android編程之退出整個(gè)應(yīng)用程序的方法,實(shí)例分析了Android直接關(guān)閉所有的Acitivity并退出應(yīng)用程序的實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-12-12