Android 视频播放器笔记之播放器实例代码
2014-10-10 12:22
465 查看
前面有文章讲到android 实现视频播放的三种方式,本文将讲述以下使用MediaPlayer + SurfaceView方法实现的思路以及相关代码。因为它自定义的优点,所以实现起来会比较麻烦。如果只想实现播放功能可以参考前文:/article/8149642.html
本例是使用MediaPlayer来控制媒体的播放,暂停,进度等,使用SufaceView显示视频内容。
一、效果展示
二、功能介绍及代码实现
可以播放、暂停、显示进度,全屏/退出全屏;需要两个自定义控件来实现功能。
1、播放视频部分:自定义一个控件继承SurfaceView(显示视频内容),实现MediaPlayControl(控制视频播放)。
SurfaceHolder是用来监控SurfaceView的变化的;当SurfaceView变化的时候会触发SurfaceHolder.Callback的实现类中的一些方法;
mediaPlayer.setDisplay(holder)是关键,也可是mediaPlayer.setDisplay(surface);此方法设置哪个surfaceView显示视频,如果没有设置会导致视频只能播放声音而无法显示图像。使用mediaPlayer播放音频的时候就无需设置这个。下图展示了如何使用mediaPlayer:
此图摘自:【Android笔记】Android
MediaPlayer的生命周期
上代码:
2、控制菜单栏(即有播放按钮、进度条、缩小按钮的那一栏)
1)进度条的更新
通过定义一个定时任务TimeTask来实现进度条的更新;
可以通过mediaPlayer.getCurrentPosition()来获取当前进度,通过mediaPlaer.getDuration()来获取总时长;
通过seekBar.seekTo();改变进度条进度。
值得注意的是,mediaPlaer.getDuration();方法只能在一定状态下使用才能得到正确的时长;
比如,Idle状态下调用getDuration()等方法是不正确的。通过new MediaPlayer()而进入Idle态不会报错,但也不会得到正确的时长,而通过reset()方法进入Idle态会导致mediaPlayer进入Error态;在Prepared态能得到正确的时长。
有兴趣的童鞋可以看一下MediaPlayer的状态图,可以避免开发中的很多错误:/article/8105927.html;
2)进度条改变更新mediaPlayer进度
2.1)使用OnSeekBarChangeListener监听SeekBar进度的改变。
2.2)使用mediaPlayer.seekTo();改变视频播放进度;
上代码:
FullScreenActivity:
CustomMediaController(菜单栏):
三、资料
MediaPlayer API 文档:/article/4602823.html
MediaPlayer 生命周期介绍:/article/8105927.html
MediaPlayer 使用方式:/article/8105925.html
本例是使用MediaPlayer来控制媒体的播放,暂停,进度等,使用SufaceView显示视频内容。
一、效果展示
二、功能介绍及代码实现
可以播放、暂停、显示进度,全屏/退出全屏;需要两个自定义控件来实现功能。
1、播放视频部分:自定义一个控件继承SurfaceView(显示视频内容),实现MediaPlayControl(控制视频播放)。
SurfaceHolder是用来监控SurfaceView的变化的;当SurfaceView变化的时候会触发SurfaceHolder.Callback的实现类中的一些方法;
mediaPlayer.setDisplay(holder)是关键,也可是mediaPlayer.setDisplay(surface);此方法设置哪个surfaceView显示视频,如果没有设置会导致视频只能播放声音而无法显示图像。使用mediaPlayer播放音频的时候就无需设置这个。下图展示了如何使用mediaPlayer:
此图摘自:【Android笔记】Android
MediaPlayer的生命周期
上代码:
package com.dream.videoplayer_v4; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaMetadataRetriever; import android.media.MediaPlayer; import android.media.MediaPlayer.OnPreparedListener; import android.net.Uri; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.widget.MediaController.MediaPlayerControl; public class CutomerVideoView extends SurfaceView implements MediaPlayerControl { private static final String TAG = CutomerVideoView.class.getSimpleName(); private MediaPlayer mediaPlayer; private SurfaceHolder surfaceHolder;// monitor changes to the surface private Callback surfaceCallBack = new Callback() {//receive the information about changes to the surface @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub surfaceHolder = null; destroyPlayer(); } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub try { surfaceHolder = holder; mediaPlayer = new MediaPlayer(); mediaPlayer.setDisplay(holder); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } }; private String uri; private OnPreparedListener onPreparedListener; public void setOnPreparedListener(OnPreparedListener onPreparedListener) { this.onPreparedListener = onPreparedListener; } public CutomerVideoView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub initView(); } public CutomerVideoView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub initView(); } public CutomerVideoView(Context context) { super(context); // TODO Auto-generated constructor stub initView(); } @SuppressWarnings("deprecation") private void initView() { try { getHolder().addCallback(surfaceCallBack); getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public boolean canPause() { // TODO Auto-generated method stub return false; } @Override public boolean canSeekBackward() { // TODO Auto-generated method stub return false; } @Override public boolean canSeekForward() { // TODO Auto-generated method stub return false; } @Override public int getAudioSessionId() { // TODO Auto-generated method stub return 0; } @Override public int getBufferPercentage() { // TODO Auto-generated method stub return 0; } @Override public int getCurrentPosition() { if (mediaPlayer != null) return mediaPlayer.getCurrentPosition(); return 0; } @Override public int getDuration() { if (mediaPlayer != null) return mediaPlayer.getDuration(); return 0; } @Override public boolean isPlaying() { if (mediaPlayer != null) return mediaPlayer.isPlaying(); return false; } @Override public void pause() { if (mediaPlayer != null) { mediaPlayer.pause(); } } @Override public void seekTo(int pos) { if (mediaPlayer != null) { mediaPlayer.seekTo(pos); } } @Override public void start() { if (mediaPlayer != null) { mediaPlayer.start(); } } private void destroyPlayer() { if (mediaPlayer != null) { mediaPlayer.reset(); mediaPlayer.release(); mediaPlayer = null; } } @SuppressLint("NewApi") public Bitmap createVideoThumbnail(String filePath) { Bitmap bm = null; MediaMetadataRetriever mmr = new MediaMetadataRetriever(); mmr.setDataSource(filePath); bm = mmr.getFrameAtTime(); return bm; } public void setVideoUri(String path) { this.uri = path; openVideo(); } public void openVideo() { try { if(mediaPlayer==null){ mediaPlayer=new MediaPlayer(); } if(uri == null){ uri=" "; } try { Log.i(TAG,"uri is :"+uri); mediaPlayer.setDataSource(getContext(), Uri.parse(uri)); } catch (Exception e) { Log.i(TAG,"setDataSource fail"); e.printStackTrace(); } mediaPlayer.prepareAsync(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setOnPreparedListener(onPreparedListener); // requestLayout(); // invalidate(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2、控制菜单栏(即有播放按钮、进度条、缩小按钮的那一栏)
1)进度条的更新
通过定义一个定时任务TimeTask来实现进度条的更新;
可以通过mediaPlayer.getCurrentPosition()来获取当前进度,通过mediaPlaer.getDuration()来获取总时长;
通过seekBar.seekTo();改变进度条进度。
值得注意的是,mediaPlaer.getDuration();方法只能在一定状态下使用才能得到正确的时长;
比如,Idle状态下调用getDuration()等方法是不正确的。通过new MediaPlayer()而进入Idle态不会报错,但也不会得到正确的时长,而通过reset()方法进入Idle态会导致mediaPlayer进入Error态;在Prepared态能得到正确的时长。
有兴趣的童鞋可以看一下MediaPlayer的状态图,可以避免开发中的很多错误:/article/8105927.html;
2)进度条改变更新mediaPlayer进度
2.1)使用OnSeekBarChangeListener监听SeekBar进度的改变。
2.2)使用mediaPlayer.seekTo();改变视频播放进度;
上代码:
FullScreenActivity:
package com.dream.videoplayer_v4; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.content.pm.ActivityInfo; import android.media.MediaPlayer; import android.media.MediaPlayer.OnPreparedListener; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.TextView; public class FullScreenActivity extends Activity { protected static final String TAG = FullScreenActivity.class.getSimpleName(); public CutomerVideoView videoView; private CustomMediaController mediaController; private ImageView iv; private ImageView playBtn; private TextView progressTv; private int duration; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); setContentView(R.layout.full_screen_video_player); initView(); } private void initView() { videoView = (CutomerVideoView) findViewById(R.id.videoview_fullscreen); videoView.setOnPreparedListener(new OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { Log.i(TAG,"enter onPrepared"); mp.setLooping(false); duration=mp.getDuration(); if(duration>0){ handler.sendEmptyMessage(1); } if(mediaController.isPlaying){ mp.start(); } } }); videoView.setVideoUri(MainActivity.URL); mediaController = (CustomMediaController) findViewById(R.id.mediacontroller_fullscreen); Timer timer=new Timer(); timer.schedule(timeTask, 0, 1000); iv = (ImageView) findViewById(R.id.iv_fullscreen); iv.setImageBitmap(videoView.createVideoThumbnail(MainActivity.URL)); playBtn = (ImageView) findViewById(R.id.playBtn); playBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { doPlay(); } }); progressTv=(TextView)findViewById(R.id.progressTv); } private void setProgressTv(int duration, int pos) { progressTv.setText(Util.parseMillionToTime(pos)+"/"+Util.parseMillionToTime(duration)); } public void doPlay() { playBtn.setVisibility(View.GONE); if(iv.getVisibility()==View.VISIBLE){ iv.setVisibility(View.GONE); videoView.openVideo(); }else{ videoView.start(); } mediaController.playOrPauseBtn.setBackgroundResource(R.drawable.pause_btn_selector); mediaController.isPlaying=true; } public void doPause(){ playBtn.setVisibility(View.VISIBLE); videoView.pause(); mediaController.isPlaying=false; } TimerTask timeTask = new TimerTask() { @Override public void run() { try { if(videoView==null || !videoView.isPlaying()) return; if(!mediaController.isPressSeekBar()){ handler.sendEmptyMessage(0); } if(videoView.getCurrentPosition()==duration && duration>0){ handler.sendEmptyMessage(0); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0: changeSeekInfo(); break; case 1: Log.i("info","duration is : "+duration); setProgressTv(duration, 0); break; } } private void changeSeekInfo() { try { int position = videoView.getCurrentPosition(); if(duration>0){ mediaController.seekTo(position, duration); setProgressTv(duration, position); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }; }; }
CustomMediaController(菜单栏):
package com.dream.videoplayer_v4; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.view.View.OnClickListener; public class CustomMediaController extends FrameLayout implements OnClickListener{ private static final String TAG = CustomMediaController.class.getSimpleName(); public ImageButton playOrPauseBtn; public ImageButton fullOrNotBtn; private SeekBar progressBar; public boolean isPlaying=false; private FullScreenActivity mAct; int mProgress; public CustomMediaController(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub initView(context); } public CustomMediaController(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub initView(context); } public CustomMediaController(Context context) { super(context); // TODO Auto-generated constructor stub initView(context); } private void initView(Context context){ mAct=(FullScreenActivity)context; View view=View.inflate(context, R.layout.media_controller, null); playOrPauseBtn=(ImageButton)view.findViewById(R.id.pauseOrPlay); fullOrNotBtn=(ImageButton)view.findViewById(R.id.fullOrNot); progressBar=(SeekBar)view.findViewById(R.id.progressBar); playOrPauseBtn.setOnClickListener(this); fullOrNotBtn.setOnClickListener(this); progressBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { mAct.videoView.seekTo(mProgress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mProgress=progress*mAct.videoView.getDuration()/seekBar.getMax(); } }); this.addView(view); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.pauseOrPlay: doPauseOrPlay(); break; case R.id.fullOrNot: doFullOrNot(); break; } } private void doPauseOrPlay() { if(isPlaying){ playOrPauseBtn.setBackgroundResource(R.drawable.play_btn_selector); isPlaying=false; mAct.doPause(); }else{ playOrPauseBtn.setBackgroundResource(R.drawable.pause_btn_selector); isPlaying=true; mAct.doPlay(); } } private void doFullOrNot() { mAct.finish(); } public void seekTo(int position,int duration){ mProgress=progressBar.getMax()*position/duration; progressBar.setProgress(mProgress); } public boolean isPressSeekBar(){ if(progressBar.isPressed()){ return true; } return false; } }
三、资料
MediaPlayer API 文档:/article/4602823.html
MediaPlayer 生命周期介绍:/article/8105927.html
MediaPlayer 使用方式:/article/8105925.html
相关文章推荐
- Android OpenCV 实例笔记3 -- 摄像头竖屏全屏的设置,更新完整代码
- [Android实例] 基于ffmpeg的Android播放器开源代码
- Android逆向实例笔记—初入so并还原分析出代码
- JUnit学习笔记2---解析controller实例代码
- Android实例剖析笔记(六)
- Android实例剖析笔记(二)
- Android实例剖析笔记(三)
- Android实例剖析笔记(一)
- Android实例剖析笔记(八)
- Android实例剖析笔记(五)
- Android实例剖析笔记(五)
- Android实例剖析笔记(七)
- 常用网页中嵌套播放器的代码实例
- Android实例剖析笔记(七)
- Android实例剖析笔记(八)
- Android实例剖析笔记(六)
- Android实例剖析笔记(一)
- Android(OPhone) 学习笔记 - 代码基础
- Android实例剖析笔记(三)
- Android实例剖析笔记(七)