利用MediaPlayer和SurfaceView编写简单的视频播放器

本文的目的是说明如何用MediaPlayer和SurfaceView来播放视频,并对项目中遇到的问题进行总结,视频编解码以及界面美化不在本文说明范围以内。

本视频播放器具备播放、暂停、停止功能,支持旋转屏幕,在发生屏幕旋转、Activity切换等事件之后,能记住之前的播放状态和播放进度。

先介绍一下在做这个项目的时候遇到的一些问题,供大家借鉴:

1.SeekBar的使用

播放器肯定需要一个能够拖动的进度条,自然会想到用SeekBar。SeekBar是从ProgressBar派生出来的,ProgressBar的方法在SeekBar上同样适用,二者不同之处在于SeekBar多了一个可以拖动的滑块,用来改变进度。

这里主要介绍OnSeekBarChangeListener接口。

1) 在创建Activity的过程中,onProgressChanged方法会先于onSurfaceCreated方法执行,如果是在onSurfaceCreated中对MediaPlayer初始化,那么在执行onProgressChanged方法的时候MediaPlayer还没有被初始化,此时如果在方法中调用MediaPlayer的某些方法(如seekTo、getCurrentPosition等)就会出错。

2) 在视频播放过程中onProgressChanged方法也会不断被调用,此时就会出现问题:视频播放时会改变进度条的进度,而进度条进度的改变也会影响到视频播放的进度。所以在onProgressChanged中需要设定判断条件,只有在拖动滑块的时候才允许通过onProgressChanged方法改变视频播放的进度,这里的判断条件就是canSeek变量。如下面代码所示:

 

		mProgressBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
				// TODO Auto-generated method stub
				canSeek=false;
				// 恢复改变SeekBar之前的播放状态
				if(isPlayingBefore)
				{
					mPlayer.start();
				}
			}
			
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
				// TODO Auto-generated method stub
				canSeek=true;
				// 记录改变SeekBar之前的播放状态
				if(mPlayer.isPlaying())
				{
					isPlayingBefore=true;
					mPlayer.pause();
				}
				else
				{
					isPlayingBefore=false;
				}
			}
			// 在创建Activity的过程中,此方法会在onSurfaceCreated之前调用一次
			// 此时MediaPlayer还没有被初始化,如果调用MediaPlayer的某些方法(如get方法)会报错
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				// TODO Auto-generated method stub
//				System.out.println("onProgressChanged");
				if(canSeek)
				{
					mPlayer.seekTo(progress);
					System.out.println("progress:"+progress);
				}
				
			}
		});


2.旋转屏幕事件
情形1:在Manifest文件中Activity标签下配置android:configChanges="orientation|screenSize",这样在旋转屏幕的时候就不会销毁Activity,可以在程序中通过onConfigurationChanged方法监听屏幕旋转事件。
情形2:默认情况,旋转屏幕会销毁当前Activity,然后重新创建Activity。
这里为了在旋转屏幕以后改变视频的尺寸以适应屏幕,选择情形2。在这一情形下,必定会执行onSaveInstanceState方法和onRestoreInstanceState方法,所以可以在这两个方法中保存和恢复数据,这里需要保存的数据主要是播放进度position和播放状态isPlayingBefore。

3.Activity切换事件
Activity切换不一定会触发onSaveInstanceState方法,所以需要在onPause方法中保存上述两个数据。结合2,3两点,将保存数据的代码写成如下形式:

@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		System.out.println("onPause");
		position=mPlayer.getCurrentPosition();
//		System.out.println("onPause: "+position);
		isPlayingBefore=mPlayer.isPlaying();
		if(mPlayer.isPlaying())
		{
			mPlayer.stop();
		}
	}
@Override
	protected void onSaveInstanceState(Bundle outState) {
		// TODO Auto-generated method stub
		super.onSaveInstanceState(outState);
		System.out.println("onSaveInstanceState");
//		System.out.println("onSaveInstanceState: "+position);
		outState.putInt("position", position);
		outState.putBoolean("isPlayingBefore", isPlayingBefore);
	}
	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onRestoreInstanceState(savedInstanceState);
		System.out.println("onRestoreInstanceState");
		
		position=savedInstanceState.getInt("position");
//		System.out.println("onRestoreInstanceState: "+position);
		isPlayingBefore=savedInstanceState.getBoolean("isPlayingBefore");
	}


4.资源释放和子线程终止
1)当Activity销毁以后需要将MediaPlayer的资源释放,以免发生意想不到的错误。
2)旋转屏幕会先销毁Activity然后重新创建,如果再Activity销毁之前没有将Timer的所有调度任务取消,那么在新的Activity创建之后,子线程仍然在执行,此时如果MediaPlayer还没有初始化,将会发生错误。所以这里采用两个手段保证子线程被终止:
第一,在onDestroy方法中取消Timer,但是取消Timer不会影响到正在执行的任务;
第二,在handleMessage方法中添加canUpdate标志,当Timer取消以后立即将canUpdate设为false,这样Timer取消以后,正在执行的子线程也无法更新进度,如下代码所示:

@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		canUpdate=false;		
		// 释放资源
		if(mPlayer!=null)
		{
			mPlayer.release();
		}
		// 取消Timer的所有子线程任务
		if(mTimer!=null)
		{
			mTimer.cancel();
		}		
		super.onDestroy();
		System.out.println("onDestroy");
	}
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what) {
// 子线程用于更新进度条
case UPDATE:
	if(canUpdate)
	{
		mProgressBar.setProgress(mPlayer.getCurrentPosition());
	}
					
	break;

	default:
	break;
}
}

 

最后附上项目源码,有比较详细的注释:

源码下载


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值