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