Android开发中的Surface库及用其制作播放器UI的例子

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就是把这些数据显示出来到屏幕上面。
两者联系如图所示:

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。
最终效果图:

我们提供了四个按钮,可以进行播放控制。

布局文件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(); 

  } 

}
(0)

相关推荐

  • android音乐播放器监听电话状态实现代码

    如下代码是监听电话的状态,代码简单不做介绍直接看代码: 复制代码 代码如下: private boolean mResumeAfterCall = false; private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {  @Override  public void onCallStateChanged(int state, String incomingNumber) {   if (state ==

  • Android应用开发之简易、大气音乐播放器实现专辑倒影效果

    今天要实现的功能是实现专辑倒影效果,这个功能已经属于图像处理方面的了,对图像处理我不怎么在行,等一下会介绍一个很实用的工具类,专门用来进行图像处理的.这个工具类不是我写的,我只是拿来用到自己的项目当中,这已经足够了,我已经完美实现我想要的效果. 效果图: < 一个很有用的工具类 /SimpleBeautyMusicPlayer/src/com/wwj/sb/utils/ImageUtil.java package com.wwj.sb.utils; import java.io.ByteArra

  • Android App中使用AudioManager类来编写音频播放器

    手机都有声音模式,声音.静音还有震动,甚至震动加声音兼备,这些都是手机的基本功能.在Android手机中,我们同样可以通过Android的SDK提供的声音管理接口来管理手机声音模式以及调整声音大小,这就是Android中AudioManager的使用. AudioManager 类位于 android.Media 包中,该类提供访问控制音量和钤声模式的操作   以下分别是AudioManager设置声音模式和调整声音大小的方法.     如何获取声音管理器: AudioManager audio

  • 教你轻松制作Android音乐播放器

    欣赏一下我们清爽的界面吧~ 如果是只用activity来制作这样的东西简直是太小儿科了,此处我们当然用的是service 首先我们先上service的代码: 1.如果我们要访问service的属性和方法,那么在activity肯定是以bindservice的方法实现的,而在service中的onbind方法也是必须要实现的,onbind返回的Ibinder对象在activity的serviceconnection中得到使用. 2.activity获取到Ibinder对象,可以进一步获取服务对象和

  • Android自定义播放器控件VideoView

    介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actvity里了,写完一看我靠代码好乱,于是就写了个自定义的播放器控件,支持指定大小,可以横竖屏切换,手动左右滑动快进快退.好了,下面开始. 效果图有点卡,我也不知道为啥..... VideoView介绍 这个是我们实现视频播放最主要的控件,详细的介绍大家百度就去看,这里介绍几个常用的方法. 用于播放视频

  • Android绘制音乐播放器示波器

    示波器是在大学的时候老师教的,但是出来工作一直没有用到过,渐渐的也就忘记了,现在重新学习一下.来看看效果图: 这里是一个自定义的柱状图,然后有一个按钮,点击按钮的时候,这里柱子会不停的运动,类似于音乐播放器里示波器的跳动. 跟前面几个自定义view的方式类似,重写了onSizeChange()方法和onDraw()方法 先列一下我们要用到的变量: /**画笔*/ private Paint mPaint; /**控件的宽度*/ private float mWidth; /**单个柱子的宽度*/

  • android暂停或停止其他音乐播放器的播放实现代码

    代码如下: 复制代码 代码如下: public static final String PLAYSTATE_CHANGED = "com.android.music.playstatechanged";    public static final String META_CHANGED = "com.android.music.metachanged";    public static final String QUEUE_CHANGED = "com

  • android webvie指定视频播放器播放网站视频

    过滤掉其他的播放器,使用我自己的播放器来做 复制代码 代码如下: wv.setWebViewClient(new WebViewClient() {            public boolean shouldOverrideUrlLoading(final WebView view,                    final String url) { if (url.contains("3gp") || url.contains("mp4")) {/

  • Android编程开发音乐播放器实例

    本文实例讲述了Android编程开发音乐播放器,分享给大家供大家参考,具体如下: 音乐播放器中综合了以下内容: SeekBar.ListView.广播接收者(以代码的形式注册Receiver).系统服务.MediaPlayer 实现的功能: 1.暂停/播放.下一首/上一首,点击某一首时播放 2.支持拖动进度条快进 3.列表排序 4.来电话时,停止播放,挂断后继续播放 5.可在后台播放 效果图: 界面: main.xml: <?xml version="1.0" encoding=

  • Android基于Service的音乐播放器

    本文开发一个基于Service的音乐播放器,音乐由后台运行的Service负责播放,当后台的播放状态发生变化时,程序将会通过发送广播通知前台Activity更新界面:当点击Activity的界面按钮时,系统将通过发送广播通知后台Service来改变播放状态. 前台Activity界面有两个按钮,分别用于控制播放/暂停.停止,另外还有两个文本框,用于显示正在播放的歌曲名.歌手名.前台Activity的代码如下: public class MainActivity extends AppCompat

随机推荐