Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發中的Surface庫及用其制作播放器UI的例子

Android開發中的Surface庫及用其制作播放器UI的例子

編輯:關於Android編程

1、Surface
1.1、 就如在C語言編程一樣,通過一個文件的句柄,就可以操作文件,獲取文件的內容。 同樣的,通過Surface就可以獲取raw buffer其中的內容。原生緩沖區(raw buffer)存儲著當前窗口的像素數據。

1.2、事實上,當得到一個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對象,可以把它當做畫布,Canvas的方法大多數是設置畫布的大小、形狀、畫布背景顏色等等,要想在畫布上面畫畫,一般要與Paint對象結合使用,顧名思義,Paint就是畫筆的風格,顏料的色彩之類的。

// 創建畫筆  
Paint paint = new Paint();  
paint.setColor(Color.RED);// 設置紅色  
 
canvas.drawCircle(60, 20, 10, paint);// 畫一個圓  

1.4、Surface本身的作用類似一個句柄,得到了這個句柄就可以得到其中的Canvas、原生緩沖器以及其它方面的內容。

1.5、Surface實現了Parcelable接口,(implements Parcelable),也就是說Surface對象可以把顯示內容的數據寫入到 Parcel 中,並且能夠從Parcel讀回數據。

2、SurfaceView
SurfaceView提供了一個專門用於繪制的surface,這個surface內嵌於。你可以控制這個Surface的格式和尺寸。Surfaceview控制這個Surface在屏幕的正確繪制位置。
surface是Z-ordered的(也就是說在xyz坐標系中,按照Z坐標排序的,Z值大的表面覆蓋在Z值小的表面的上方),這表明它總在自己所在窗口的後面。surfaceview在顯示窗口處為Surface提供了一個可見區域,通過這個區域,才能看到Surface裡面的內容。可以放置一些覆蓋圖層(overlays)在Surface上面,如Button、Textview之類的。但是,需要注意的是,如果Surface上面有全透明的控件,那麼隨著Surface的每一次變化,這些全透明的控件就會重新渲染,這樣的話,就影響性能與顯示的效果。
你可以通過SurfaceHolder這個接口去訪問Surface,而執行getHolder()方法可以得到SurfaceHolder接口。
當SurfaceView的窗口可見時,Surface就會被創建,當SurfaceView窗口隱藏時,Surface就會被銷毀。當然了,你也可以通過復寫surfaceCreated(SurfaceHolder) 和 surfaceDestroyed(SurfaceHolder)  這兩個方法來驗證一下Surface何時被創建與何時被銷毀。
SurfaceView提供了一個運行在渲染線程的surface,若你要更新屏幕,你需要了解以下線程知識。
所有SurfaceView 和 SurfaceHolder.Callback的方法都應該在主線程(UI線程)裡面調用,應該要確保渲染進程所訪問變量的同步性。
你必須確保只有當Surface有效的時候,(也就是當Surface的生命周期在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之間)才能讓渲染進程訪問。

2.1、SurfaceView與Surface的聯系
簡單來說,SurfaceView與Surface的聯系就是,Surface是管理顯示內容的數據(implementsParcelable),包括存儲於數據的交換。而SurfaceView就是把這些數據顯示出來到屏幕上面。
兩者聯系如圖所示:

2016412145144865.png (1024×654)

3、SurfaceHolder
SurfaceHolder是控制surface的一個抽象接口,你可以通過SurfaceHolder來控制surface的尺寸和格式,或者修改surface的像素,監視surface的變化等等,SurfaceHolder是SurfaceView的典型接口。
與直接控制SurfaceView來修改surface不同,使用SurfaceHolder來修改surface時,需要注意lockCanvas() 和Callback.surfaceCreated().這兩個方法。
SurfaceHolder控制surface的流程所使用的幾個方法。

3.1、abstract void    addCallback(SurfaceHolder.Callback callback)
給SurfaceHolder一個回調對象。

3.2、abstract Canvas    lockCanvas(Rect dirty)
鎖定畫布中的某一個區域,返回的畫布對象Canvas(當更新的內容只有一個區域時,同時要追求高效,可以只更
新一部分的區域,而不必更新全部畫布區域)

3.3、abstract Canvas    lockCanvas()
鎖定畫布,返回的畫布對象Canvas

3.4、abstract void    removeCallback(SurfaceHolder.Callback callback)
移除回調對象

3.5、abstract void    unlockCanvasAndPost(Canvas canvas)
結束鎖定畫圖,並提交改變。

4、SurfaceHolder.Callback
SurfaceHolder.Callback是監聽surface改變的一個接口

4.1、public abstract voidsurfaceChanged(SurfaceHolder holder, int format, int width, int height)
surface發生改變時被調用

4.2、public abstract voidsurfaceCreated(SurfaceHolder holder)
在surface創建時被調用,一般在這個方法裡面開啟渲染屏幕的線程。

4.3、public abstract voidsurfaceDestroyed(SurfaceHolder holder)
銷毀時被調用,一般在這個方法裡將渲染的線程停止。

附上上述所說幾種的聯系方法

SurfaceHolder = SurfaceView.getHolder(); 
 
Surface = SurfaceHolder.getSurface(); 
 
Canvas =SurfaceHolder.LockCanvas(Rect dirty) 
 
Canvas  =Surface.lockCanvas(Rect dirty) 

5、DEMO:通過SurfaceView以及SurfaceHolder進行視頻播放
使用AudioView進行視頻播放的時候,是不是很不爽,千篇一律的模式,惡心吧。這裡,我們可以通過一些方式對MediaPlayer進行包裝。而所用到的正是SurfaceView以及SurfaceHolder。
最終效果圖:

2016412145237293.gif (356×299)

我們提供了四個按鈕,可以進行播放控制。

布局文件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實現自己的播放器 
 * @author chenzheng_java 
 * @since 2011/03/23 
 * @description 這裡進行一下補充說明,我們可以通過mediaplayer添加OnPreparedListener 
 * 以及OnCompletionListener等事件對准備好播放以及播放完成後的操作進行控制。 
 * 使用SurfaceView以及SurfaceHolder進行視頻播放時,結構是這樣的: 
 * 1、首先,我們從布局文件中獲取一個surfaceView 
 * 2、通過surfaceView.getHolder()方法獲取與該容器想對應的surfaceHolder 
 * 3、對srufaceHolder進行一些默認的設置,如addCallback()和setType() 
 * 4、通過mediaPlayer.setDisplay()方法將視頻播放與播放容器鏈接起來 
 */ 
public class MyMediaPlayerActivity extends Activity { 
 
  MediaPlayer mediaPlayer ; // 播放器的內部實現是通過MediaPlayer 
  SurfaceView surfaceView ;// 裝在視頻的容器 
  SurfaceHolder surfaceHolder;// 控制surfaceView的屬性(尺寸、格式等)對象 
  boolean isPause ; // 是否已經暫停了 
   
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.media); 
    surfaceView = (SurfaceView) findViewById(R.id.surfaceView1); 
    /** 
     * 獲取與當前surfaceView相關聯的那個的surefaceHolder 
     */ 
    surfaceHolder = surfaceView.getHolder(); 
    /** 
     * 注冊當surfaceView創建、改變和銷毀時應該執行的方法 
     */ 
    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被改變了"); 
      } 
    }); 
     
    /** 
     * 這裡必須設置為SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS哦,意思 
     * 是創建一個push的'surface',主要的特點就是不進行緩沖 
     */ 
    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(); 
    // 設置多媒體流類型 
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
     
    // 設置用於展示mediaPlayer的容器 
    mediaPlayer.setDisplay(surfaceHolder); 
    try { 
      mediaPlayer.setDataSource("/data/jinsha.3gp"); 
      mediaPlayer.prepare(); 
      mediaPlayer.start(); 
      isPause = false; 
    } catch (Exception e) { 
      Log.i("通知", "播放過程中出現了錯誤哦"); 
    } 
  } 
   
  /** 
   * 暫停 
   */ 
  private void pause(){ 
    Log.i("通知", "點擊了暫停按鈕"); 
    if(isPause==false){ 
      mediaPlayer.pause(); 
      isPause=true; 
    }else{ 
      mediaPlayer.start(); 
      isPause=false; 
    } 
     
     
  } 
   
  /** 
   * 重置 
   */ 
  private void reset(){ 
    Log.i("通知", "點擊了reset按鈕"); 
    // 跳轉到視頻的最開始 
    mediaPlayer.seekTo(0); 
    mediaPlayer.start(); 
  } 
   
  /** 
   * 停止 
   */ 
  private void stop(){ 
    Log.i("通知", "點擊了stop按鈕"); 
    mediaPlayer.stop(); 
    mediaPlayer.release(); 
     
  } 
   
} 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved