Android详细教程(基础篇):三十、Android 多媒体播放MediaPlayer详解

时间:2024-03-23 19:14:43

简介

可以使用MediaPlayer类播放Mp3以及视频。

public class

MediaPlayer

extends Object

java.lang.Object

   

android.media.MediaPlayer

本部分所要讲解的就是MediaPlayer类的使用。

音频和视频有许多种格式,但是MediaPlayer类只能完成一些基本格式的播放操作,而一些比较特殊的格式,如rmvb,需要单独的解码器。

对播放音频/视频文件和流的控制是通过一个状态机制来管理的。下图显示一个MediaPlayer对象被支持的播放控制操作驱动的生命周期和状态。椭圆代表MediaPlayer对象可能驻留的状态。弧线表示驱动MediaPlayer在各个状态之间迁移的播放控制操作。这里有两种类型的弧线。由一个箭头开始的弧代表同步的方法调用,而以双箭头开头的代表的弧线代表异步方法调用。

MediaPlayer生命周期:

 

Android详细教程(基础篇):三十、Android 多媒体播放MediaPlayer详解

通过这张图,我们可以知道一个MediaPlayer对象有以下的状态:

1)当一个MediaPlayer对象被刚刚用new操作符创建或是调用了reset()方法后,它就处于Idle状态。当调用了release()方法后,它就处于End状态。这两种状态之间是MediaPlayer对象的生命周期。

1.1) 在一个新构建的MediaPlayer对象和一个调用了reset()方法的MediaPlayer对象之间有一个微小的但是十分重要的差别。在处于Idle状态时,调用

getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioStreamType(int), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(int), prepare() 或者 prepareAsync() 方法都是编程错误。当一个MediaPlayer对象刚被构建的时候,内部的播放引擎和对象的状态都没有改变,在这个时候调用以上的那些方法,框架将无法回调客户端程序注册的OnErrorListener.onError()方法;但若这个MediaPlayer对象调用了reset()方法之后,再调用以上的那些方法,内部的播放引擎就会回调客户端程序注册的OnErrorListener.onError()方法了,并将错误的状态传入。

 

1.2) 我们建议,一旦一个MediaPlayer对象不再被使用,应立即调用release()方法来释放在内部的播放引擎中与这个MediaPlayer对象关联的资源。资源可能包括如硬件加速组件的单态组件,若没有调用release()方法可能会导致之后的MediaPlayer对象实例无法使用这种单态硬件资源,从而退回到软件实现或运行失败。一旦MediaPlayer对象进入了End状态,它不能再被使用,也没有办法再迁移到其它状态。

1.3) 此外,使用new操作符创建的MediaPlayer对象处于Idle状态,而那些通过重载的create()便利方法创建的MediaPlayer对象却不是处于Idle状态。事实上,如果成功调用了重载的create()方法,那么这些对象已经是Prepare状态了。 

2) 在一般情况下,由于种种原因一些播放控制操作可能会失败,如不支持的音频/视频格式,缺少隔行扫描的音频/视频,分辨率太高,流超时等原因,等等。因此,错误报告和恢复在这种情况下是非常重要的。有时,由于编程错误,在处于无效状态的情况下调用了一个播放控制操作可能发生。在所有这些错误条件下,内部的播放引擎会调用一个由客户端程序员提供的OnErrorListener.onError()方法。客户端程序员可以通过调用MediaPlayer.setOnErrorListener(android.media.MediaPlayer.OnErrorListener)方法来注册OnErrorListener.

2.1) 一旦发生错误,MediaPlayer对象会进入到Error状态。

2.2) 为了重用一个处于Error状态的MediaPlayer对象,可以调用reset()方法来把这个对象恢复成Idle状态。

2.3) 注册一个OnErrorListener来获知内部播放引擎发生的错误是好的编程习惯。

2.4) 在不合法的状态下调用一些方法,如prepare(),prepareAsync()和setDataSource()方法会抛出IllegalStateException异常。 

3) 调用setDataSource(FileDescriptor)方法,或setDataSource(String)方法,或setDataSource(Context,Uri)方法,或setDataSource(FileDescriptor,long,long)方法会使处于Idle状态的对象迁移到Initialized状态。

3.1) 若当此MediaPlayer处于其它的状态下,调用setDataSource()方法,会抛出IllegalStateException异常。

3.2) 好的编程习惯是不要疏忽了调用setDataSource()方法的时候可能会抛出的IllegalArgumentException异常和IOException异常。 

4) 在开始播放之前,MediaPlayer对象必须要进入Prepared状态。

4.1) 有两种方法(同步和异步)可以使MediaPlayer对象进入Prepared状态:要么调用prepare()方法(同步),此方法返回就表示该MediaPlayer对象已经进入了Prepared状态;要么调用prepareAsync()方法(异步),此方法会使此MediaPlayer对象进入Preparing状态并返回,而内部的播放引擎会继续未完成的准备工作。当同步版本返回时或异步版本的准备工作完全完成时就会调用客户端程序员提供的OnPreparedListener.onPrepared()监听方法。可以调用MediaPlayer.setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)方法来注册OnPreparedListener.

4.2) Preparing是一个中间状态,在此状态下调用任何具备边影响的方法的结果都是未知的!

4.3) 在不合适的状态下调用prepare()和prepareAsync()方法会抛出IllegalStateException异常。当MediaPlayer对象处于Prepared状态的时候,可以调整音频/视频的属性,如音量,播放时是否一直亮屏,循环播放等。 

5) 要开始播放,必须调用start()方法。当此方法成功返回时,MediaPlayer的对象处于Started状态。isPlaying()方法可以被调用来测试某个MediaPlayer对象是否在Started状态。

5.1) 当处于Started状态时,内部播放引擎会调用客户端程序员提供的OnBufferingUpdateListener.onBufferingUpdate()回调方法,此回调方法允许应用程序追踪流播放的缓冲的状态。

5.2) 对一个已经处于Started 状态的MediaPlayer对象调用start()方法没有影响。

6) 播放可以被暂停,停止,以及调整当前播放位置。当调用pause()方法并返回时,会使MediaPlayer对象进入Paused状态。注意Started与Paused状态的相互转换在内部的播放引擎中是异步的。所以可能需要一点时间在isPlaying()方法中更新状态,若在播放流内容,这段时间可能会有几秒钟。

6.1) 调用start()方法会让一个处于Paused状态的MediaPlayer对象从之前暂停的地方恢复播放。当调用start()方法返回的时候,MediaPlayer对象的状态会又变成Started状态。

6.2) 对一个已经处于Paused状态的MediaPlayer对象pause()方法没有影响。

7) 调用stop()方法会停止播放,并且还会让一个处于Started,Paused,Prepared或PlaybackCompleted状态的MediaPlayer进入Stopped状态。

7.1) 对一个已经处于Stopped状态的MediaPlayer对象stop()方法没有影响。

8) 调用seekTo()方法可以调整播放的位置。

8.1) seekTo(int)方法是异步执行的,所以它可以马上返回,但是实际的定位播放操作可能需要一段时间才能完成,尤其是播放流形式的音频/视频。当实际的定位播放操作完成之后,内部的播放引擎会调用客户端程序员提供的OnSeekComplete.onSeekComplete()回调方法。可以通过setOnSeekCompleteListener(OnSeekCompleteListener)方法注册。

8.2) 注意,seekTo(int)方法也可以在其它状态下调用,比如Prepared,Paused和PlaybackCompleted状态。此外,目前的播放位置,实际可以调用getCurrentPosition()方法得到,它可以帮助如音乐播放器的应用程序不断更新播放进度

9) 当播放到流的末尾,播放就完成了。

9.1) 如果调用了setLooping(boolean)方法开启了循环模式,那么这个MediaPlayer对象会重新进入Started状态。

9.2) 若没有开启循环模式,那么内部的播放引擎会调用客户端程序员提供的OnCompletion.onCompletion()回调方法。可以通过调用MediaPlayer.setOnCompletionListener(OnCompletionListener)方法来设置。内部的播放引擎一旦调用了OnCompletion.onCompletion()回调方法,说明这个MediaPlayer对象进入了PlaybackCompleted状态。

9.3) 当处于PlaybackCompleted状态的时候,可以再调用start()方法来让这个MediaPlayer对象再进入Started状态。

序号

状态

解释

1

Idle

当使用关键字new实例化一个MediaPlayer对象或者是调用了类中的reset()方法会进入此状态。

2

End

当调用release()方法之后将进入到此状态,此时会释放掉所有占用的硬件和软件资源,并且不会再进入到其他的任何一种状态了。

3

Initialized

当MediaPlayer对象设置好了要播放的媒体文件(setDataSource())之后进入到此状态。

4

Prepared

进入到预播放状态(prepare()、prepareAsync()),进入到此状态则表示目前的媒体文件没有任何的问题,可以使用OnPrepareListener监听此状态。
1.如果用户调用的是prepare()方法(同步)则表示该MediaPlayer对象已经进入到了Prepared状态;
2.如果用户调用的是prepareAsync()方法(异步),则表示该MediaPlayer对象进入到了Preparing状态并返回,而内部播放引擎会继续执行未完成的准备操作。

5

Started

正在进行每日播放(start()),此时可以使用seekTo()方法指定

6

Paused

 

7

Stop

 

8

PlaybackCompleted

 

9

Error

 

 

5.2. 播放音频

Mp3是一个比较通用的播放格式,而MediaPlayer类可以实现这样的操作功能。如果现在这个Mp3文件是保存在项目之中,那么就建立一个res/raw的文件夹,或者是将其建立在sdcard上,并且使用文件进行读取,现在使用的是前者。

实例:

在res下新建一文件夹raw,把待播放文件放到此raw文件夹下:

Android详细教程(基础篇):三十、Android 多媒体播放MediaPlayer详解

配置:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context=".MediaPlayerActivity" >

    <TextView

        android:id="@+id/info"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="音频播放器" />

    <LinearLayout

        android:layout_width="match_parent"

       android:layout_height="wrap_content"

       android:orientation="horizontal">

                <ImageButton

                    android:id="@+id/play"

                    android:src="@drawable/play"

                    android:layout_width="wrap_content"

                    android:layout_height="wrap_content"

                    android:text="开始" />

                <ImageButton

                    android:id="@+id/pause"

                    android:src="@drawable/pause"

                    android:layout_width="wrap_content"

                    android:layout_height="wrap_content"

                    android:text="暂停" />

                <ImageButton

                    android:id="@+id/stop"

                    android:src="@drawable/stop"

                    android:layout_width="wrap_content"

                    android:layout_height="wrap_content"

                    android:text="停止" />

    </LinearLayout>

    <SeekBar

        android:id="@+id/seekBar"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"/>

</LinearLayout>

Activity:

package com.makyan.demo;

import android.app.Activity;

import android.media.MediaPlayer;

import android.media.MediaPlayer.OnCompletionListener;

import android.os.AsyncTask;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.ImageButton;

import android.widget.SeekBar;

import android.widget.TextView;

import android.widget.SeekBar.OnSeekBarChangeListener;

public class MediaPlayerActivity extends Activity {

            private ImageButton play = null;                                                                        // 图片按钮

            private ImageButton pause = null;                                                                      // 图片按钮

            private ImageButton stop = null;                                                                        // 图片按钮

            private TextView info = null;                                                                                           // 文本显示组件

            private MediaPlayer myMediaPlayer = null;                                                      // 媒体播放

            private boolean pauseFlag = false;                                                                                // 暂停播放标记

            private boolean playFlag = true ;                                                                        // 是否播放的标记

            private SeekBar seekbar = null;                                                                                      // 拖动条

            @Override

            public void onCreate(Bundle savedInstanceState) {

                       super.onCreate(savedInstanceState);

                       super.setContentView(R.layout.activity_media_player);                      // 调用布局文件

                       this.info = (TextView) super.findViewById(R.id.info);                // 取得组件

                       this.play = (ImageButton) super.findViewById(R.id.play);         // 取得组件

                       this.pause = (ImageButton) super.findViewById(R.id.pause);  // 取得组件

                       this.stop = (ImageButton) super.findViewById(R.id.stop);        // 取得组件

                       this.seekbar = (SeekBar) super.findViewById(R.id.seekBar);    // 取得组件

                       this.play.setOnClickListener(new PlayOnClickListenerImpl()) ; // 按钮单击事件

                       this.pause.setOnClickListener(new PauseOnClickListenerImpl());       // 按钮单击事件

                       this.stop.setOnClickListener(new StopOnClickListenerImpl()); // 按钮单击事件

            }

            private class PlayOnClickListenerImpl implements OnClickListener {

                       @Override

                       public void onClick(View view) {

                                   MediaPlayerActivity.this.myMediaPlayer = MediaPlayer.create(

                                                          MediaPlayerActivity.this, R.raw.luoqi_sxsy);                  // 找到指定的资源

                                   MediaPlayerActivity.this.myMediaPlayer

                                                          .setOnCompletionListener(new OnCompletionListener() {

                                                                      @Override

                                                                      public void onCompletion(MediaPlayer media) {

                                                                                  MediaPlayerActivity.this.playFlag = false ;          // 播放完毕

                                                                                 media.release();                                                     // 释放所有状态

                                                                      }

                                                          });                                                                                         // 播放完毕监听

                                   MediaPlayerActivity.this.seekbar.setMax(MediaPlayerActivity.this.myMediaPlayer

                                                          .getDuration());                                           // 设置拖动条长度为媒体长度

                                   UpdateSeekBar update = new UpdateSeekBar() ;                      // 启动子线成更新拖动条

                                   update.execute(1000) ;                                                                                        // 休眠1

                                   MediaPlayerActivity.this.seekbar.setOnSeekBarChangeListener(

                                                          new OnSeekBarChangeListenerImpl());   // 拖动条改变音乐播放位置

                                   if (MediaPlayerActivity.this.myMediaPlayer != null) {

                                               MediaPlayerActivity.this.myMediaPlayer.stop();            // 停止播放

                                   }

                                   try {

                                               MediaPlayerActivity.this.myMediaPlayer.prepare();     // 进入到预备状态

                                               MediaPlayerActivity.this.myMediaPlayer.start(); // 播放文件

                                               MediaPlayerActivity.this.info.setText("正在播放音频文件...");          // 设置文字

                                   } catch (Exception e) {

                                               MediaPlayerActivity.this.info.setText("文件播放出现异常," + e);// 设置文字

                                   }

                       }

            }

            private class UpdateSeekBar extends AsyncTask<Integer, Integer, String> {

                       @Override

                       protected void onPostExecute(String result) {                           // 任务执行完后执行

                       }

                       @Override

                       protected void onProgressUpdate(Integer... progress) {           // 每次更新之后的数值

                                   MediaPlayerActivity.this.seekbar.setProgress(progress[0]) ;// 更新拖动条

                       }

                       @Override

                       protected String doInBackground(Integer... params) {  // 处理后台任务

                                   while (MediaPlayerActivity.this.playFlag) {                                 // 进度条累加

                                               try {

                                                          Thread.sleep(params[0]);                                                  // 延缓执行

                                               } catch (InterruptedException e) {

                                                          e.printStackTrace();

                                               }

                                               this.publishProgress(MediaPlayerActivity.this.myMediaPlayer

                                                                      .getCurrentPosition());                                                      // 修改拖动条

                                   }

                                   return null;                                                                                                            // 返回执行结果

                       }

            }

            private class OnSeekBarChangeListenerImpl implements OnSeekBarChangeListener {

                       @Override

                       public void onProgressChanged(SeekBar seekBar,

                                               int progress, boolean fromUser) {

                       }

                       @Override

                       public void onStartTrackingTouch(SeekBar seekBar) {

                       }

                       @Override

                       public void onStopTrackingTouch(SeekBar seekBar) {               // 进度条停止拖拽

                                   MediaPlayerActivity.this.myMediaPlayer.seekTo(seekBar

                                                          .getProgress());                                           // 定义播放位置

                       }

            }

            private class PauseOnClickListenerImpl implements OnClickListener {

                       @Override

                       public void onClick(View view) {

                                   if (MediaPlayerActivity.this.myMediaPlayer != null) {

                                               if (MediaPlayerActivity.this.pauseFlag) {                         // true表示由暂停变为播放

                                                          MediaPlayerActivity.this.myMediaPlayer.start(); // 播放文件

                                                          MediaPlayerActivity.this.pauseFlag = false;        // 修改标记位

                                               } else {                                                         // false表示由播放变为暂停

                                                          MediaPlayerActivity.this.myMediaPlayer.pause(); // 暂停播放

                                                          MediaPlayerActivity.this.pauseFlag = true;         // 修改标记位

                                               }

                                   }

                       }

            }

 

            private class StopOnClickListenerImpl implements OnClickListener {

                       @Override

                       public void onClick(View view) {

                                   if (MediaPlayerActivity.this.myMediaPlayer != null) {

                                               MediaPlayerActivity.this.myMediaPlayer.stop();            // 停止播放

                                               MediaPlayerActivity.this.info.setText("停止播放音频文件...");

                                   }

                       }

            }

}

 

5.3. SurfaceView、Callback、SurfaceHolder 详解

SurfaceView:

public class

SurfaceView

extends View

java.lang.Object

   

android.view.View

 

   

android.view.SurfaceView

Known Direct Subclasses

GLSurfaceViewRSSurfaceViewVideoView

Callback:

public static interface

SurfaceHolder.Callback

android.view.SurfaceHolder.Callback

Known Indirect Subclasses

GLSurfaceViewNativeActivityRSSurfaceViewSurfaceHolder.Callback2

SurfaceHolder:

public interface

SurfaceHolder

android.view.SurfaceHolder

先看一段代码:

package com.makyan.demo;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.view.SurfaceHolder;

import android.view.SurfaceHolder.Callback;

import android.view.SurfaceView;

import android.view.animation.Animation;

public class MySurfaceView extends SurfaceView implements Callback,Runnable{

            private int screenW;//屏幕跨度

            private int screenH;//屏幕高度

            private SurfaceHolder sfh;

            private Thread thread;

            private Paint paint;

            public MySurfaceView(Context context) {

                       super(context);

                       sfh = this.getHolder();

                       thread = new Thread();

                       paint = new Paint();

                       paint.setColor(Color.RED);

                       paint.setAntiAlias(true);

                       this.setKeepScreenOn(true);

            }

            @Override

            public void surfaceChanged(SurfaceHolder holder, int format, int width,

                                   int height) {

            }

            @Override

            public void surfaceCreated(SurfaceHolder holder) {

                       screenW = this.getWidth();

                       screenH = this.getHeight();

                       thread.start();

            }

            @Override

            public void surfaceDestroyed(SurfaceHolder holder) {

            }

            @Override

            public void run() {

                       while(true){

                                   draw();

                                   try {

                                               Thread.sleep(1000);

                                   } catch (InterruptedException e) {

                                               e.printStackTrace();

                                   }

                       }                     

            }

            private void draw(){

                       Canvas canvas = sfh.lockCanvas();

                       canvas.drawColor(Color.WHITE);

                       canvas.drawText("makyan的画室", 100, 100, paint);

                       canvas.drawText("这就是简单的游戏框架", 100, 130, paint);

                       sfh.unlockCanvasAndPost(canvas);// 将画好的画布提交

            }

            @Override

            public void startAnimation(Animation animation) {

                       super.startAnimation(animation);

            }

}

代码很简单,我们继承继承surfaceview类,并且使用回调callback接口以及线程runnable接口。那么这里我简单的说下Callback接口和SurfaceHolder 类的作用;

//备注1

  callback接口:

 只要继承SurfaceView类并实现SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView了,SurfaceHolder.Callback在底层的Surface状态发生变化的时候通知View,SurfaceHolder.Callback具有如下的接口:

  •  surfaceCreated(SurfaceHolder holder):当Surface第一次创建后会立即调用该函数。程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface。,一般在该方法中启动绘图的线程。
  •  surfaceChanged(SurfaceHolder holder, int format, int width,int height):当Surface的状态(大小和格式)发生变化的时候会调用该函数,在surfaceCreated调用后该函数至少会被调用一次。
  • surfaceDestroyed(SurfaceHolder holder) :surface被销毁的时候调用,如退出游戏画面,一般在该方法中停止绘图线程。

SurfaceHolder 类:

它是一个用于控制surface的接口,它提供了控制surface 的大小,格式,上面的像素,即监视其改变的。 

SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface 就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或则Canvas lockCanvas()函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑或则尚未创建调用该函数会返回null,在 unlockCanvas() 和 lockCanvas()中Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect rect)函数来指定一个rect区域,这样该区域外的内容会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在Surface绘制过程中不会被改变(被摧毁、修改)。

// 备注2

我没有在该surfaceview的初始化函数中将其 ScreenW 与 ScreenH 进行赋值,这里要特别注意,如果你在初始化调用ScreenW = this.getWidth();和ScreenH = this.getHeight();那么你将得到很失望的值 全部为0;原因是和接口Callback接口机制有关,当我们继承callback接口会重写它的surfaceChanged()、surfaceCreated()、surfaceDestroyed(),这几个函数当surfaceCreated()被执行的时候,真正的view才被创建,也就是说之前得到的值为0 ,是因为初始化会在surfaceCreated()方法执行以前执行,view没有的时候我们去取屏幕宽高肯定是0,所以这里要注意这一点;

//备注3

这里我把draw的代码都try起来,主要是为了当画的内容中一旦抛出异常了,那么我们也能 在finally中执行该操作。这样当代码抛出异常的时候不会导致Surface出去不一致的状态。  

 其实这就是一个简单的游戏架构了,当然还少了按键处理,声音播放等等,这些我后续会写出相关的学习文章。对于surfaceview的介绍差不多就介绍到这里了,其中的理解是看了别人的文章和自己的理解、当然可能理解的会有些偏差,但是我想不会太离谱 呵呵。

5.4. 播放视频--VideoView

Android详细教程(基础篇):三十、Android 多媒体播放MediaPlayer详解

以上是某个视频的讲解,显然,讲解视频播放的时候sdk还是老版本,现在播放视频完全可以使用继承了SurfaceView的VideoView

如果要想操作SurfaceView,那么就必须对其做一些基本的设置,设置它的操作类型等参数,设置这些参数,又得依靠SurfaceHolder类来完成。

实例—、通过SurfaceView来播放视频:

配置:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

            android:orientation="vertical"

            android:layout_width="fill_parent"

            android:layout_height="fill_parent">

            <LinearLayout

                       android:orientation="horizontal"

                       android:layout_width="wrap_content"

                       android:layout_height="wrap_content">

                       <ImageButton

                                   android:id="@+id/play"

                                   android:layout_width="wrap_content"

                                   android:layout_height="wrap_content"

                                   android:src="@drawable/play" />

                       <ImageButton

                                   android:id="@+id/stop"

                                   android:layout_width="wrap_content"

                                   android:layout_height="wrap_content"

                                   android:src="@drawable/stop" />

            </LinearLayout>

            <SurfaceView

                       android:id="@+id/surfaceView"

                       android:layout_width="fill_parent"

                       android:layout_height="fill_parent" />

</LinearLayout>

Activity:

package com.makyan.deno;

import android.app.Activity;

import android.media.AudioManager;

import android.media.MediaPlayer;

import android.os.Bundle;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.ImageButton;

public class ViewPlayerActivity extends Activity {

            private ImageButton play = null;

            private ImageButton stop = null;

            private MediaPlayer media = null;

            private SurfaceView surfaceView = null;

            private SurfaceHolder surfaceHolder = null;

            @Override

            public void onCreate(Bundle savedInstanceState) {

                       super.onCreate(savedInstanceState);

                       super.setContentView(R.layout.activity_view_player);             // 调用布局文件

                       this.play = (ImageButton) super.findViewById(R.id.play);

                       this.stop = (ImageButton) super.findViewById(R.id.stop);

                       this.surfaceView = (SurfaceView) super.findViewById(R.id.surfaceView);

                       this.surfaceHolder = this.surfaceView.getHolder();                   // 取得SurfaceHolder

                       this.surfaceHolder.setType(

                                               SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);           // 设置SurfaceView的类型

                       this.media = new MediaPlayer();                                                            // 创建MediaPlayer对象

                       try {

                                   this.media.setDataSource("/sdcard/huilai.3gp");                      // 设置播放文件的路径

                       } catch (Exception e) {

                                   e.printStackTrace();

                       }

                       this.play.setOnClickListener(new PlayOnClickListenerImpl());// 单击事件

                       this.stop.setOnClickListener(new StopOnClickListenerImpl());// 单击事件

            }

            private class PlayOnClickListenerImpl implements OnClickListener {

                       @Override

                       public void onClick(View arg0) {

                                   ViewPlayerActivity.this.media.setAudioStreamType(

                                                          AudioManager.STREAM_MUSIC);                                    // 设置音频类型

                                   ViewPlayerActivity.this.media.setDisplay(

                                                          ViewPlayerActivity.this.surfaceHolder);   // 设置显示的区域

                                   try {

                                               ViewPlayerActivity.this.media.prepare();            // 预备状态

                                               ViewPlayerActivity.this.media.start();                  // 播放视频

                                   } catch (Exception e) {

                                               e.printStackTrace();

                                   }

                       }

            }

            private class StopOnClickListenerImpl implements OnClickListener {

                       @Override

                       public void onClick(View arg0) {

                                   ViewPlayerActivity.this.media.stop();                              // 停止播放

                       }

            }

}

实例二、通过VideoView播放视频

继承关系:

public class VideoView extends SurfaceView

implements MediaController.MediaPlayerControl

用于播放视频文件。 VideoView 类可以从不同的来源(例如资源文件或内容提供器) 读取图像,计算和维护视频的画面尺寸以使其适用于任何布局管理器, 并提供一些诸如缩放、着色之类的显示选项。

构造函数:

public VideoView (Context context)

创建一个默认属性的VideoView实例。

参数

context 视图运行的应用程序上下文,通过它可以访问当前主题、资源等等。

public VideoView (Context context, AttributeSet attrs)

创建一个带有attrs属性的VideoView实例。

参数

context 视图运行的应用程序上下文,通过它可以访问当前主题、资源等等。

attrs 用于视图的 XML 标签属性集合。

public VideoView (Context context, AttributeSet attrs, int defStyle)

创建一个带有attrs属性,并且指定其默认样式的VideoView实例。

参数

context 视图运行的应用程序上下文,通过它可以访问当前主题、资源等等。

attrs 用于视图的 XML 标签属性集合。

defStyle 应用到视图的默认风格。如果为 0 则不应用(包括当前主题中的)风格。 该值可以是当前主题中的属性资源,或者是明确的风格资源 ID。

公共方法

public boolean canPause ()

(译者注:判断是否能够暂停播放视频)

public boolean canSeekBackward ()

(译者注:判断是否能够倒退)

public boolean canSeekForward ()

(译者注:判断是否能够快进)

public int getBufferPercentage ()

(译者注:获得缓冲区的百分比)

public int getCurrentPosition ()

(译者注:获得当前的位置)

public int getDuration ()

(译者注:获得所播放视频的总时间)

public boolean isPlaying ()

(译者注:判断是否正在播放视频)

public boolean onKeyDown (int keyCode, KeyEvent event)

KeyEvent.Callback.onKeyMultiple() 的默认实现。如果视图可用并可按, 当按下 KEYCODE_DPAD_CENTER 或 KEYCODE_ENTER 时执行视图的按下事件。

参数

keyCode 表示按下的键的、在 KEYCODE_ENTER 中定义的键盘代码

event KeyEvent 对象,定义了按钮动作

返回值

如果处理了事件,返回真。如果允许下一个事件接受器处理该事件,可以返回假

public boolean onTouchEvent (MotionEvent ev)

实现该方法来处理触屏事件。

参数

event 触屏事件

返回值

如果事件已经处理返回True,否则返回false。

public boolean onTrackballEvent (MotionEvent ev)

实现这个方法去处理轨迹球的动作事件,轨迹球相对于上次事件移动的位置能用MotionEvent.getX()MotionEvent.getY()函数取回。对应用户按下一次方向键, 他们通常作为一次移动处理(为了表现来自轨迹球的更小粒度的移动信息,他们返回小数)。

参数event 动作的事件

public void pause ()

(译者注:使得播放暂停)

public int resolveAdjustedSize (int desiredSize, int measureSpec)

(译者注:取得调整后的尺寸。如果measureSpec对象传入的模式是UNSPECIFIED那么返回的是desiredSize。如果measureSpec对象传入的模式是AT_MOST,返回的将是desiredSize和measureSpec对象的尺寸两者中最小的那个。如果measureSpec对象传入的模式是EXACTLY,那么返回的是measureSpec对象中的尺寸大小值。

 

关于MeasureSpec类的额外说明:MeasureSpec是一个android.view.View的内部类。它封装了从父类传送到子类的布局要求信息。每个MeasureSpec对象描述了控件的高度或者宽度。MeasureSpec对象是由尺寸和模式组成的,有3个模式:UNSPECIFIED、EXACTLY、AT_MOST,这个对象由MeasureSpec.makeMeasureSpec()函数创建。)

public void resume ()

(译者注:恢复挂起的播放器)

public void seekTo (int msec)

(译者注:设置播放位置)

public void setMediaController (MediaController controller)

(译者注:设置媒体控制器)

public void setOnCompletionListener (MediaPlayer.OnCompletionListener l)

注册在媒体文件播放完毕时调用的回调函数。

参数

l 要执行的回调函数

public void setOnErrorListener (MediaPlayer.OnErrorListener l)

注册在设置或播放过程中发生错误时调用的回调函数。如果未指定回调函数, 或回调函数返回假,VideoView 会通知用户发生了错误。

参数

l 要执行的回调函数

public void setOnPreparedListener (MediaPlayer.OnPreparedListener l)

注册在媒体文件加载完毕,可以播放时调用的回调函数。

参数

l 要执行的回调函数

public void setVideoPath (String path)

(译者注:设置视频文件的路径名)

public void setVideoURI (Uri uri)

(译者注:设置视频文件的统一资源标识符)

public void start ()

(译者注:开始播放视频文件)

public void stopPlayback ()

(译者注:停止回放视频文件)

public void suspend ()

(译者注:挂起视频文件的播放)

而真正的使用很简单,如下:

VideoView videoView = (VideoView) this.findViewById(R.id.videoView);

MediaController mc = new MediaController(this);

videoView.setMediaController(mc);

videoView.setVideoURI(Uri uri);

videoView.requestFocus();

videoView.start();

5.5使用摄像头拍照

SurfaceView最大的特点就是提供了一个高速的更新空间

Callback:

public static interface

SurfaceHolder.Callback

android.view.SurfaceHolder.Callback

Known Indirect Subclasses

GLSurfaceViewNativeActivityRSSurfaceViewSurfaceHolder.Callback2

 

Android详细教程(基础篇):三十、Android 多媒体播放MediaPlayer详解

SurfaceHolder.Callback接口中定义的方法

Android详细教程(基础篇):三十、Android 多媒体播放MediaPlayer详解

Camera:

除了拍照的预览界面之外的重要组成组件就是进行调用摄像头的操作类Camera,此类主要负责完成拍照图片的参数设置及保存。

public class

Camera

extends Object

java.lang.Object

   

android.hardware.Camera

访问照相机,你必须声明相机在你的Android Manifest权限。也一定要包括<使用功能>清单元素声明您的应用程序使用的相机功能。例如,如果您使用相机和自动对焦

     <uses-feature android:name="android.hardware.camera" />

            <uses-feature android:name="android.hardware.camera.autofocus" />

            <uses-permission android:name="android.permission.CAMERA" />

            <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

Camera类的常用操作方法

Android详细教程(基础篇):三十、Android 多媒体播放MediaPlayer详解

Camera类中定义的内部接口

Android详细教程(基础篇):三十、Android 多媒体播放MediaPlayer详解

实例:

配置:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

            xmlns:android="http://schemas.android.com/apk/res/android"

            android:orientation="vertical"

            android:layout_width="fill_parent"

            android:layout_height="fill_parent">

            <Button

                       android:id="@+id/but"

                       android:layout_width="fill_parent"

                       android:layout_height="wrap_content"

                       android:text="照相" />

            <SurfaceView

                       android:id="@+id/surface"

                       android:layout_width="fill_parent"

                       android:layout_height="wrap_content" />

</LinearLayout>

Activity:

package com.makyan.demo;

import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import android.app.Activity;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.PixelFormat;

import android.hardware.Camera;

import android.hardware.Camera.AutoFocusCallback;

import android.hardware.Camera.Parameters;

import android.hardware.Camera.PictureCallback;

import android.hardware.Camera.ShutterCallback;

import android.os.Bundle;

import android.os.Environment;

import android.view.Display;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import android.view.View;

import android.view.View.OnClickListener;

import android.view.Window;

import android.view.WindowManager;

import android.widget.Button;

import android.widget.Toast;

 

public class CameraActivity extends Activity {

            private SurfaceView surface = null;                                                                                // SurfaceView

            private SurfaceHolder holder = null;                                                                  // SurfaceHolder

            private Camera cam = null;                                                                                             // 拍照组件

            private Button but = null;                                                                                                // 按钮组件

            private boolean previewRunning = true;                                                           // 预览结束的标记

            @Override

            public void onCreate(Bundle savedInstanceState) {

                       super.onCreate(savedInstanceState);

                       super.requestWindowFeature(Window.FEATURE_NO_TITLE); // 不显示标题

                       super.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

                                               WindowManager.LayoutParams.FLAG_FULLSCREEN);  // 全屏显示

                       super.getWindow().addFlags(

                                               WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 高亮显示

                       super.setContentView(R.layout.activity_camera);                                                       // 布局管理器

                       this.but = (Button) super.findViewById(R.id.but);                      // 取得组件

                       this.surface = (SurfaceView) findViewById(R.id.surface);// 取得组件

                       this.holder = surface.getHolder();                                                           // 设置Holder

                       this.holder.addCallback(new MySurfaceViewCallback());         // 加入回调

                       this.holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 设置缓冲类型

                       this.holder.setFixedSize(500, 350);                                                                     // 设置分辨率

                       this.but.setOnClickListener(new OnClickListenerImpl());          // 单击事件

 

            }

            // 接口SurfaceHolder.Callback被用来接收摄像头预览界面变化的信息。

            private class MySurfaceViewCallback implements SurfaceHolder.Callback {

                       public void surfaceChanged(SurfaceHolder holder, int format, int width,

                                               int height) {              // 当预览界面的格式和大小发生改变时,该方法被调用

                       }

                       public void surfaceCreated(SurfaceHolder holder) {      // 初次实例化,预览界面被创建时,该方法被调用

                                   CameraActivity.this.cam = Camera.open(0);                              // 取得摄像头

                                   WindowManager manager = (WindowManager) CameraActivity.this

                                                          .getSystemService(Context.WINDOW_SERVICE); // 取得窗口服务

                                   Display display = manager.getDefaultDisplay(); // 取得Display对象

                                   Parameters param = CameraActivity.this.cam.getParameters(); // 取得照相机参数

                                   param.setPreviewSize(display.getWidth(), display.getHeight()); // 设置预览大小

                                   param.setPreviewFrameRate(5);                                                 // 每秒显示5帧的数据

                                   param.setPictureFormat(PixelFormat.JPEG);                 // 设置图片格式

                                   param.set("jpeg-quality", 85);                                                      // 设置图片质量,最高为100

                                   CameraActivity.this.cam.setParameters(param);          // 设置参数

                                   try {                                                                                     // 通过SurfaceView显示

                                               CameraActivity.this.cam

                                                                      .setPreviewDisplay(CameraActivity.this.holder);

                                   } catch (IOException e) {

                                               e.printStackTrace();

                                   }

                                   CameraActivity.this.cam.startPreview();                        // 开始预览

                                   CameraActivity.this.previewRunning = true;                 // 修改预览标记

                        }

                       public void surfaceDestroyed(SurfaceHolder holder) { // 当预览界面被关闭时,该方法被调用

                                   if (CameraActivity.this.cam != null) {

                                               if (CameraActivity.this.previewRunning) {            // 如果正在预览

                                                          CameraActivity.this.cam.stopPreview(); // 停止预览

                                                          CameraActivity.this.previewRunning = false; // 修改标记

                                               }

                                               // 摄像头只能被一个Activity程序使用,所以要释放设像头。

                                               CameraActivity.this.cam.release();                                  // 释放摄像头

                                   }

                       }

            }

            private PictureCallback jpgcall = new PictureCallback() {

                       public void onPictureTaken(byte[] data, Camera camera) {

                                   try {

                                               Bitmap bmp = BitmapFactory

                                                                      .decodeByteArray(data, 0, data.length);  // 定义BitMap

                                               String fileName = Environment.getExternalStorageDirectory()

                                                                      .toString()

                                                                      + File.separator

                                                                      + "makyanphoto"

                                                                      + File.separator

                                                                      + "MAKYAN_"

                                                                      + System.currentTimeMillis()

                                                                      + ".jpg";                                 // 输出文件名称

                                               File file = new File(fileName);                                                       // 定义File对象

                                               if (!file.getParentFile().exists()) {                           // 父文件夹不存在

                                                          file.getParentFile().mkdirs();                                            // 创建父文件夹

                                               }

                                               BufferedOutputStream bos = new BufferedOutputStream(

                                                                      new FileOutputStream(file));                                // 使用字节缓存流

                                               bmp.compress(Bitmap.CompressFormat.JPEG, 80, bos);          // 图片压缩

                                               bos.flush();                                                                                                 // 清空缓冲

                                               bos.close();                                                                                                // 关闭

                                               Toast.makeText(CameraActivity.this,

                                               "拍照成功,照片已保存在" + fileName + "文件之中", Toast.LENGTH_SHORT)

                                                                      .show();                                                                                          // 显示Toast

                                               CameraActivity.this.cam.stopPreview();                         // 停止预览

                                               CameraActivity.this.cam.startPreview();                        // 开始预览

                                   } catch (Exception e) {

                                   }

                       }

            };

            private class OnClickListenerImpl implements OnClickListener {

                       public void onClick(View v) {

                                   if (CameraActivity.this.cam != null) {                                           // 存在Camera对象

                                               CameraActivity.this.cam.autoFocus(new AutoFocusCallbackImpl());  // 自动对焦

                                   }

                       }

            }

 

            private class AutoFocusCallbackImpl implements AutoFocusCallback {

                       public void onAutoFocus(boolean success, Camera cam) {

                                   if (success) {                                                                                                          // 如果对焦成功

                                               CameraActivity.this.cam.takePicture(sc, pc, jpgcall);     // 获取图片

                                               CameraActivity.this.cam.stopPreview();                         // 停止预览                       

                                   }

                       }

            }

            private ShutterCallback sc = new ShutterCallback() {

                       public void onShutter() {

                                   // 按下快门后的回调函数

                       }

            };

            private PictureCallback pc = new PictureCallback() {

 

                       public void onPictureTaken(byte[] arg0, Camera arg1) {

                                   // 保存的源图片数据

                       }

            };

}

 

详解:

摄像头预览相片变化的信息:

private class MySurfaceViewCallback implements SurfaceHolder.Callback {

                       public void surfaceChanged(SurfaceHolder holder, int format, int width,

                                               int height) {                                                                                    // 当预览界面的格式和大小发生改变时,该方法被调用

                       }

 

                       public void surfaceCreated(SurfaceHolder holder) {      // 初次实例化,预览界面被创建时,该方法被调用

                                   CameraActivity.this.cam = Camera.open(0);                              // 取得摄像头

                                   WindowManager manager = (WindowManager) CameraActivity.this

                                                          .getSystemService(Context.WINDOW_SERVICE); // 取得窗口服务

                                   Display display = manager.getDefaultDisplay(); // 取得Display对象

                                   Parameters param = CameraActivity.this.cam.getParameters(); // 取得照相机参数

                                   param.setPreviewSize(display.getWidth(), display.getHeight()); // 设置预览大小

                                   param.setPreviewFrameRate(5);                                                 // 每秒显示5帧的数据

                                   param.setPictureFormat(PixelFormat.JPEG);                 // 设置图片格式

                                   param.set("jpeg-quality", 85);                                                      // 设置图片质量,最高为100

                                   CameraActivity.this.cam.setParameters(param);          // 设置参数

                                   try {                                                                                     // 通过SurfaceView显示

                                               CameraActivity.this.cam

                                                                      .setPreviewDisplay(CameraActivity.this.holder);

                                   } catch (IOException e) {

                                               e.printStackTrace();

                                   }

                                   CameraActivity.this.cam.startPreview();                        // 开始预览

                                   CameraActivity.this.previewRunning = true;                 // 修改预览标记

 

                       }

 

                       public void surfaceDestroyed(SurfaceHolder holder) { // 当预览界面被关闭时,该方法被调用

                                   if (CameraActivity.this.cam != null) {

                                               if (CameraActivity.this.previewRunning) {            // 如果正在预览

                                                          CameraActivity.this.cam.stopPreview(); // 停止预览

                                                          CameraActivity.this.previewRunning = false; // 修改标记

                                               }

                                               // 摄像头只能被一个Activity程序使用,所以要释放设像头。

                                               CameraActivity.this.cam.release();                                  // 释放摄像头

                                   }

                       }

            }

当按下拍照按钮后,进行拍照并保存照片:

private class OnClickListenerImpl implements OnClickListener {

                       public void onClick(View v) {

                                   if (CameraActivity.this.cam != null) {                                           // 存在Camera对象

                                               CameraActivity.this.cam.autoFocus(new AutoFocusCallbackImpl());  // 自动对焦

                                   }

                       }

            }

 

            private class AutoFocusCallbackImpl implements AutoFocusCallback {

 

                       public void onAutoFocus(boolean success, Camera cam) {

                                   if (success) {                                                                                   // 如果对焦成功

                                               CameraActivity.this.cam.takePicture(sc, pc, jpgcall);   // 获取图片

                                               CameraActivity.this.cam.stopPreview();                         // 停止预览                       

                                   }

                       }

 

            }

            //获取照片并保存

            private PictureCallback jpgcall = new PictureCallback() {

                       public void onPictureTaken(byte[] data, Camera camera) {

                                   try {

                                               Bitmap bmp = BitmapFactory

                                                                      .decodeByteArray(data, 0, data.length);  // 定义BitMap

                                               String fileName = Environment.getExternalStorageDirectory()

                                                                      .toString()

                                                                      + File.separator

                                                                      + "makyanphoto"

                                                                      + File.separator

                                                                      + "MAKYAN_"

                                                                      + System.currentTimeMillis()

                                                                      + ".jpg";                                                                    // 输出文件名称

                                               File file = new File(fileName);                                                       // 定义File对象

                                               if (!file.getParentFile().exists()) {                           // 父文件夹不存在

                                                          file.getParentFile().mkdirs();                                            // 创建父文件夹

                                               }

                                               BufferedOutputStream bos = new BufferedOutputStream(

                                                                      new FileOutputStream(file));                                // 使用字节缓存流

                                               bmp.compress(Bitmap.CompressFormat.JPEG, 80, bos);          // 图片压缩

                                               bos.flush();                                                                                                 // 清空缓冲

                                               bos.close();                                                                                                // 关闭

                                               Toast.makeText(CameraActivity.this,

                                                                      "拍照成功,照片已保存在" + fileName + "文件之中", Toast.LENGTH_SHORT)

                                                                      .show();                                                                                          // 显示Toast

                                               CameraActivity.this.cam.stopPreview();                         // 停止预览

                                               CameraActivity.this.cam.startPreview();                        // 开始预览

                                   } catch (Exception e) {

                                   }

                       }

            };