MediaPlayer简单使用,绑定surfaceView实现播放视频的功能

转载自

Android MediaPlayer使用方法简单介绍

播放音频

  android中播放音频可以使用MediaPlayer类来实现,一下是它的一些方法:

              方法名             功能描述
            setDataSource()        设置要播放的音频文件的位置。
            prepare()            在开始播放之前调用这个方法完成准备工作。
            start()               开始或继续播放音频。
            pause()               暂停播放音频。
            reset()              将 MediaPlayer 对象重置到刚刚创建的状态。
            seekTo()             从指定的位置开始播放音频。
            stop()                     停止播放音频。调用这个方法后的 MediaPlayer 对象无法再播放音频。
            release()             释放掉与 MediaPlayer 对象相关的资源。
            isPlaying()             判断当前 MediaPlayer 是否正在播放音频。
            getDuration()           获取载入的音频文件的时长。

 在安卓系统中,播放音频最简单的方法是使用内置的媒体播放器(MediaPlayer类)。MediaPlayer既可以播放本地文件,也可以播放来自URL的网络数据流。
1)如何获得MediaPlayer实例:
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式,如:
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//这时就不用调用setDataSource了
 
2) 如何设置要播放的文件:
MediaPlayer要播放的文件主要包括3个来源:
a. 用户在应用中事先自带的resource资源
例如:MediaPlayer.create(this, R.raw.test);
b. 存储在SD卡或其他文件路径下的媒体文件
例如:mp.setDataSource("/sdcard/test.mp3");
 
c. 网络上的媒体文件
例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");
 
MediaPlayer的setDataSource一共四个方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
 
其中使用FileDescriptor时,需要将文件放到与res文件夹平级的assets文件夹里,然后使用:
AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());
来设置datasource
 
3)对播放器的主要控制方法:
Android通过控制播放器的状态的方式来控制媒体文件的播放,其中:
prepare()和prepareAsync() 提供了同步和异步两种方式设置播放器进入prepare状态,需要注意的是,如果MediaPlayer实例是由create方法创建的,那么第一次启动播放前不需要再调用prepare()了,因为create方法里已经调用过了。
start()是真正启动文件播放的方法,
pause()和stop()比较简单,起到暂停和停止播放的作用,

seekTo()是定位方法,可以让播放器从指定的位置开始播放,需要注意的是该方法是个异步方法,也就是说该方法返回时并不意味着定位完成,尤其是播放的网络文件,真正定位完成时会触发OnSeekComplete.onSeekComplete(),如果需要是可以调用setOnSeekCompleteListener(OnSeekCompleteListener)设置监听器来处理的。
release()可以释放播放器占用的资源,一旦确定不再使用播放器时应当尽早调用它释放资源。
 
reset()可以使播放器从Error状态中恢复过来,重新会到Idle状态。在重置MediaPlayer的时候使用,重置完,重新setDataResource prepare start,播放下一首
 
4)设置播放器的监听器:
MediaPlayer提供了一些设置不同监听器的方法来更好地对播放器的工作状态进行监听,以期及时处理各种情况,
如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、
setOnErrorListener(MediaPlayer.OnErrorListener listener)等,设置播放器时需要考虑到播放器可能出现的情况设置好监听和处理逻辑,以保持播放器的健壮性。

1、创建MediaPlayer实例

有两种创建MediaPlayer实例的办法,

办法1:

MediaPlayer player = MediaPlayer.Create(this, Resource.Raw.test);

用MediaPlayer.Create创建实例时可直接指定数据源。

办法2:

MediaPlayer player = new MediaPlayer();

2、数据源

有3种。

(1)播放raw文件夹下的资源文件

应用程序自带的音乐资源一般保存到raw文件夹下,下面的代码演示了如何播放Resource/raw下的媒体文件:

MediaPlayer mPlayer=MediaPlayer.Create(this, Resource.Raw.MyAudio);

mPlayer.Start();

注意:这种方式主要适用于播放应用程序自带的容量较小的资源文件,例如接听电话时播放的音乐、按键音、开机启动音、信息提示音等都可以随应用程序一块发布。

(2)播放来自文件系统的媒体文件

指播放Android默认的系统标准文件夹下、Assets文件夹下、SD卡中的媒体文件,这些文件都属于文件系统。

以下描述了使用MediaPlayer从文件系统中播放音频的步骤:

  • 初始化MediaPlayer对象。
  • 通过SetDataSource方法(同步方式)或者SetDataSourceAsync方法(异步方式)设置要播放的文件。
  • 调用Prepare方法(同步方式)或者PrepareAsync方法(异步方式)初始化播放器。
  • 调用Start方法开始音频播放。
  • 注意采用这种方式时,播放顺序非常重要:先设置数据源、同步播放设备,最后再调用Start方法播放。

利用MediaPlayer与surfaceView相结合实现视频播放的效果

加载本地资源
/**加载本地资源
 * 主要步骤:
 * 1.创建MediaPlayer播放器
 *      1.MediaPlayer.create(this,"本地资源文件");
 * 2.得到SurfaceHolder对象,surfaceView.getHolder()
 * 3.surfaceHolder绑定生命周期  surfaceHodler.addCallBack(new CallBack(){...})
 * 4.在surfaceHolder监听的回调里面,与视频播放器进行绑定(surfaceCreated)
 *      mediaPlayer.setDisplay(surfaceHolder)
 *  5.视频播放器播放视频
 *      mediaPlayer.start();
 */
加载网络资源
/**
 * 加载网络资源
 *主要步骤:
 * 1.创建MediaPlayer播放器
 *  new MediaPlayer();
 * 2.设置数据源
 *      mediaPlayer.setDataSource(context,uri);
 *    //  meidaPlayer.setDataSource(path)
 *  3. .得到SurfaceHolder对象,surfaceView.getHolder()
 *  4.surfaceHolder绑定生命周期  surfaceHodler.addCallBack(new CallBack(){...})
 *  5.在surfaceHolder监听的回调里面,与视频播放器进行绑定(surfaceCreated)
 *      mediaPlayer.setDisplay(surfaceHolder)
 *   6.准备
 *      mediaPlayer.prepareAsync()//异步准备
 *      mediaPlayer.prepare()//同步准备
 *   7.设置准备监听器
 *             mediaPlayer.setOnPreparedListener(new PreparedListener(){});
 *   8.在准备监听器里开启mediaPlayer
 *   mediaPlayer.start();
 *
 *
 *   //注意:
    1.加载网络资源出现了bug,原因:设置异步准备时,会调用准备监听器来开启媒体播放器,同时主线程也会继续执行,通过播放器获取时长时,会出现媒体播放器无效的错误,因为播放器还没有开启播放.
      解决:将异步准备后的操作都放在准备监听里面实现,
    2.meidiaPlayer调用stop方法后可以再次播放视频,但需要用reset让meidiaPlayer对象回到初始状态,再重新设置资源 *
*/

xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context="fanggao.qf.surfaceview_vedioplayer.MainActivity">
    <FrameLayout
        android:id="@+id/fl_layout"
        android:layout_width="match_parent"
        android:layout_height="300dp">
        <SurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="match_parent"
            android:layout_height="300dp" />
        <ImageView
            android:id="@+id/iv_play"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@android:drawable/ic_media_play"/>
    </FrameLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/fl_layout">
        <ImageView
            android:id="@+id/iv_play_controll"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:src="@android:drawable/ic_media_play"/>
        <SeekBar
            android:id="@+id/sb_progress"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"/>
        <TextView
            android:id="@+id/tv_time"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="00:00:00/00:00:00"
            android:gravity="center_vertical"/>
    </LinearLayout>
</RelativeLayout>
View Code

案列分析:

/**
 * 1.初始化控件
 *      0.初始化媒体播放器
 *          1.准备资源,并创建播放器
 *      1.找到surfaceView,并创建生命周期
 *          1.创建surfaceView
 *              1.surfaceView跟视频播放控件绑定
 *              2.销毁surfaceView
 *                  释放资源
 *           2.播放图标显示
 *           3.控制台显示播放图标
 *      2.初始化事件
 *         1.点击播放按钮-变为播放
 *              1.当前播放按钮隐藏
 *              2.控制条显示暂停图标
 *              3.视频开始播放
 *
 *           2.点击控制台
 *              1.点击播放
 *                    1.控制台显示暂停图标
 *                    2.隐藏当前的播放按钮
 *                    3.视频开始播放
 *              2.点击暂停
 *                  1.控制台按钮显示播放图标
 *                   2..当前播放按钮显示
     *              3.视频暂停播放
     *          3.拖到进度条
 *                  1.跳到对应的位置播放
 *                  2.更新时间
 *
 *           3.初始化动态视图
 *              2.更新控制台显示
 *                 1..拿到视频总长度
 *                  1.开启线程
 *                      1.更新seekBar进度
 *                          1.拿到当前进度
 *                          2.计算当前seekBar的值
 *                          3.发送到主线程
 *                          4.更新
 *                              1.更新seekBar的值
 *                              2.更新时间进度
 *                                  1s更新一次
 *                  3.播放完成设置监听
 *            4.释放资源
 */

源代码:

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback,View.OnClickListener,SeekBar.OnSeekBarChangeListener,MediaPlayer.OnCompletionListener{
    /*显示视频资源*/
    private SurfaceView surfaceView;
    /*拖动条显示当前进度*/
    private SeekBar sb_progress;
    /*surface的控制器,用来操纵surfaceView。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。*/
    private SurfaceHolder holder;
    /*媒体播放器播放音频*/
    private MediaPlayer mediaPlayer;
    //显示暂停播放图标
    private ImageView imgPlay;
    //控制台显示暂停播放图标
    private ImageView imgControll;
    /*视频总长度*/
    private int duration;
    /*handler发送的成功的标识*/
    private final int UPDATE_PROGRESS = 0;
    /*让线程开始和结束的标记*/
    private boolean flag;
    /*设置显示的图片*/
    private TextView tvTime;
    /*更新进度条和时间*/
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case UPDATE_PROGRESS:
                    int currentPosition = (int) msg.obj;
                    Log.i("tag", "handleMessage: "+currentPosition);
                    //更新seekBar
                    sb_progress.setProgress(currentPosition);
                    //更新时间
                    updateTime(currentPosition,duration);
                break;
                default:
                    break;
            }

        }
    };
    private UpdateThread updateThread;

    /**
     * 更新时间
     * @param currentPosition
     * @param duration
     */
    private void updateTime(int currentPosition, int duration) {
       tvTime.setText(formatTime(currentPosition)+"/"+formatTime(duration));
    }

    /**
     * 格式化时间
     * @param time
     * @return
     */
    private String formatTime(int time) {//格式化时间为00:00:00
        //两位数,小于两位数自动补0 例 09:09:00
        String format = "%02d:%02d:%02d";
        //time为ms
        int hour = time/1000/3600;
        int minute = time/1000%3600/60;
        int second = time/1000%60;
        return String.format(format,hour,minute,second);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化视频资源
        mediaPlayer =MediaPlayer.create(this,R.raw.hello);
        initView();
        initEvent();

        //获取视频的总长度(ms)
         duration = mediaPlayer.getDuration();
        //为seekBar设置初始值
        sb_progress.setMax(duration);


    }

    /**
     * 进度条拖动监听
     * @param seekBar
     * @param progress
     * @param fromUser
     */
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        //播放器跳到对应位置
        mediaPlayer.seekTo(seekBar.getProgress());
    }

    /**
     * 播放完成的时候监听
     * @param mp
     */
    @Override
    public void onCompletion(MediaPlayer mp) {
        //重置
        int position = 0;
        //让视频跳到最开始并暂停
        mp.seekTo(position);
        Message obtain = Message.obtain();
        obtain.what = UPDATE_PROGRESS;
        obtain.obj = position;
        handler.sendMessage(obtain);
        imgPlay.setVisibility(View.VISIBLE);
        imgControll.setImageResource(android.R.drawable.ic_media_play);
    }

    class UpdateThread extends Thread{
    @Override
    public void run(){
        while(flag){
            //获得当前的进度
            int currentPosition = mediaPlayer.getCurrentPosition();
            Message message = Message.obtain();
            message.what = UPDATE_PROGRESS;
            //计算seekbar的值
            message.obj = currentPosition;
            //handler发送进度
            handler.sendMessage(message);
            //1s一次
            SystemClock.sleep(1000);
        }
    }
}

    private void initEvent() {
        //暂停按钮点击监听,点击播放视频或者暂停视频
        imgControll.setOnClickListener(this);
        imgPlay.setOnClickListener(this);
        //拖动监听,跳到对应的位置
        sb_progress.setOnSeekBarChangeListener(this);
        //播放完成监听,重置播放器,进度
        mediaPlayer.setOnCompletionListener(this);
    }

    private void initView() {
        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        sb_progress = (SeekBar) findViewById(R.id.sb_progress);
        imgPlay= (ImageView) findViewById(R.id.iv_play);
        imgControll = (ImageView) findViewById(R.id.iv_play_controll);
        tvTime = (TextView) findViewById(R.id.tv_time);
//surfaceView绑定生命周期
         holder = surfaceView.getHolder();
         holder.addCallback(this);
        //设置默认值
        imgPlay.setVisibility(View.VISIBLE);
        imgControll.setImageResource(android.R.drawable.ic_media_play);
    }

    /**
     * surfaceViewa绑定生命周期需要回调的方法
     * @param holder
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //播放器绑定surfaceView
        mediaPlayer.setDisplay(holder);
        //开启线程
       updateThread = new UpdateThread();
        updateThread.start();
        flag = true;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        //关闭线程,释放资源
        flag = false;
        if(mediaPlayer != null){
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.iv_play:
                imgPlay.setVisibility(View.GONE);
                imgControll.setImageResource(android.R.drawable.ic_media_pause);
                mediaPlayer.start();
            break;
            case R.id.iv_play_controll:
                if(mediaPlayer.isPlaying()){//正在播放--暂停按钮
                    imgPlay.setVisibility(View.VISIBLE);
                    imgControll.setImageResource(android.R.drawable.ic_media_play);
                    mediaPlayer.pause();
                }else{//暂停播放
                    imgPlay.setVisibility(View.GONE);
                    imgControll.setImageResource(android.R.drawable.ic_media_pause);
                    mediaPlayer.start();
                }
            break;
        }
    }

    @Override
    protected void onDestroy() {
        //
        super.onDestroy();
    }
}

效果:

 

原文地址:https://www.cnblogs.com/fangg/p/5931356.html