Android開發(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(畫布)對(duì)象。這一點(diǎn)可以通過(guò)查看\frameworks\base\core\java\android\view\Surface.java文件可知道Surface類定義了一個(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)做畫布,Canvas的方法大多數(shù)是設(shè)置畫布的大小、形狀、畫布背景顏色等等,要想在畫布上面畫畫,一般要與Paint對(duì)象結(jié)合使用,顧名思義,Paint就是畫筆的風(fēng)格,顏料的色彩之類的。
// 創(chuàng)建畫筆 Paint paint = new Paint(); paint.setColor(Color.RED);// 設(shè)置紅色 canvas.drawCircle(60, 20, 10, paint);// 畫一個(gè)圓
1.4、Surface本身的作用類似一個(gè)句柄,得到了這個(gè)句柄就可以得到其中的Canvas、原生緩沖器以及其它方面的內(nèi)容。
1.5、Surface實(shí)現(xiàn)了Parcelable接口,(implements Parcelable),也就是說(shuō)Surface對(duì)象可以把顯示內(nèi)容的數(shù)據(jù)寫入到 Parcel 中,并且能夠從Parcel讀回?cái)?shù)據(jù)。
2、SurfaceView
SurfaceView提供了一個(gè)專門用于繪制的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之類的。但是,需要注意的是,如果Surface上面有全透明的控件,那么隨著Surface的每一次變化,這些全透明的控件就會(huì)重新渲染,這樣的話,就影響性能與顯示的效果。
你可以通過(guò)SurfaceHolder這個(gè)接口去訪問(wèn)Surface,而執(zhí)行g(shù)etHolder()方法可以得到SurfaceHolder接口。
當(dāng)SurfaceView的窗口可見(jiàn)時(shí),Surface就會(huì)被創(chuàng)建,當(dāng)SurfaceView窗口隱藏時(shí),Surface就會(huì)被銷毀。當(dāng)然了,你也可以通過(guò)復(fù)寫surfaceCreated(SurfaceHolder) 和 surfaceDestroyed(SurfaceHolder) 這兩個(gè)方法來(lái)驗(yàn)證一下Surface何時(shí)被創(chuàng)建與何時(shí)被銷毀。
SurfaceView提供了一個(gè)運(yùn)行在渲染線程的surface,若你要更新屏幕,你需要了解以下線程知識(shí)。
所有SurfaceView 和 SurfaceHolder.Callback的方法都應(yīng)該在主線程(UI線程)里面調(diào)用,應(yīng)該要確保渲染進(jìn)程所訪問(wèn)變量的同步性。
你必須確保只有當(dāng)Surface有效的時(shí)候,(也就是當(dāng)Surface的生命周期在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之間)才能讓渲染進(jìn)程訪問(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)
鎖定畫布中的某一個(gè)區(qū)域,返回的畫布對(duì)象Canvas(當(dāng)更新的內(nèi)容只有一個(gè)區(qū)域時(shí),同時(shí)要追求高效,可以只更
新一部分的區(qū)域,而不必更新全部畫布區(qū)域)
3.3、abstract Canvas lockCanvas()
鎖定畫布,返回的畫布對(duì)象Canvas
3.4、abstract void removeCallback(SurfaceHolder.Callback callback)
移除回調(diào)對(duì)象
3.5、abstract void unlockCanvasAndPost(Canvas canvas)
結(jié)束鎖定畫圖,并提交改變。
4、SurfaceHolder.Callback
SurfaceHolder.Callback是監(jiān)聽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è)方法里面開啟渲染屏幕的線程。
4.3、public abstract voidsurfaceDestroyed(SurfaceHolder holder)
銷毀時(shí)被調(diào)用,一般在這個(gè)方法里將渲染的線程停止。
附上上述所說(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)建、改變和銷毀時(shí)應(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)建一個(gè)push的'surface',主要的特點(diǎn)就是不進(jìn)行緩沖
*/
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
/***
* @param targetButton 被用戶點(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è)置多媒體流類型
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)到視頻的最開始
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編程開發(fā)音樂(lè)播放器實(shí)例
- android音樂(lè)播放器監(jiān)聽電話狀態(tài)實(shí)現(xiàn)代碼
- Android應(yīng)用開發(fā)之簡(jiǎn)易、大氣音樂(lè)播放器實(shí)現(xiàn)專輯倒影效果
- Android App中使用AudioManager類來(lái)編寫音頻播放器
- Android繪制音樂(lè)播放器示波器
- Android基于Service的音樂(lè)播放器
相關(guān)文章
Flutter將整個(gè)App變?yōu)榛疑暮?jiǎn)單實(shí)現(xiàn)方法
Flutter?是?Google?開源的?UI?工具包,幫助開發(fā)者通過(guò)一套代碼庫(kù)高效構(gòu)建多平臺(tái)精美應(yīng)用,這篇文章主要給大家介紹了關(guān)于Flutter將整個(gè)App變?yōu)榛疑膶?shí)現(xiàn)方法,在Flutter中實(shí)現(xiàn)整個(gè)App變?yōu)榛疑欠浅:?jiǎn)單的,需要的朋友可以參考下2021-12-12
超簡(jiǎn)單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼
這篇文章主要介紹了超簡(jiǎn)單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
Kotlin如何安全訪問(wèn)lateinit變量的實(shí)現(xiàn)
這篇文章主要介紹了Kotlin如何安全訪問(wèn)lateinit變量的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
cocos2d-2.0-x-2.0.3 交叉編譯到android報(bào)錯(cuò)解決
我用的是cocos2d-2.0-x-2.0.3 之前弄了一天也沒(méi)成功 今天來(lái)了下載了最新的ndk8 更新了sdk 又重新是了一遍 居然成功了,不知道是工具的版本問(wèn)題還是哪一步出錯(cuò)誤了,在這里詳細(xì)的整理一下,感興趣的朋友可以了解下2013-01-01
Android多線程處理機(jī)制中的Handler使用介紹
本文將為大家介紹下Android的Handler的使用方法,Handler可以發(fā)送Messsage和Runnable對(duì)象到與其相關(guān)聯(lián)的線程的消息隊(duì)列,感興趣的朋友可以了解下哈2013-06-06
安卓應(yīng)用開發(fā)通過(guò)java調(diào)用c++ jni的圖文使用方法
這篇文章主要介紹了2013-11-11
Android實(shí)現(xiàn)簡(jiǎn)易版彈鋼琴效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)易版彈鋼琴效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
Android使用Intent實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了Android使用Intent實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10

