您的位置:首页 > 移动开发 > Android开发

Android多媒体之视频播放器高级开发

2016-03-02 23:35 435 查看

1.获取播放的数据源

播放视频的数据源一般有两个,一个是请求网络,从服务器后台直接获取播放的视频信息,另一种是播放手机中本地的视频,这里我们采用的播放源为播放手机本地的视频

1.1 查询获取手机中的视频的信息

1.1.1 查询方法一

定义要查询到的视频的信息,包括视频的名称,视频大小,视频播放时长以及在手机中的储存位置

String[] projection = {Media._ID,Media.TITLE,Media.SIZE,Media.DURATION,Media.DATA};


进行数据的查询操作

Cursor cursor = getActivity().getContentResolver().query(Media.EXTERNAL_CONTENT_URI, projection, null, null, null);


这里查询到的信息是包含在cursor对象中的

1.1.2 查询方法二

SimpleQueryHandler	queryHandler = new SimpleQueryHandler(getActivity().getContentResolver());

String[] projection = {Media._ID,Media.TITLE,Media.SIZE,Media.DURATION,Media.DATA};queryHandler.startQuery(0, adapter, Media.EXTERNAL_CONTENT_URI, projection, null, null, null);


public class SimpleQueryHandler  extends AsyncQueryHandler{
public SimpleQueryHandler(ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
super.onQueryComplete(token, cookie, cursor);
if(cookie!=null && cookie instanceof CursorAdapter){
CursorAdapter adapter = (CursorAdapter) cookie;
adapter.changeCursor(cursor);//相当于notify
}
}
}


边里是调用的异步查询的方法,查询到的视频的相关信息也是包含在cursor中的,其中传入的adapter对象是继承于CursorAdapter

1.1.3 建立保存视频信息的info对象

public class VideoItem implements Serializable{
public String title;//视频的名称
public long size;//视频的大小
public long duration;//视频的时长
public String path;//视频在手机中的路径

/**
* 将cursor中的数据封装为一个info
* @param cursor
* @return
*/
public static VideoItem fromCursor(Cursor cursor){
VideoItem videoItem = new VideoItem();
videoItem.duration=cursor.getLong(cursor.getColumnIndex(Media.DURATION)));
videoItem.path=cursor.getString(cursor.getColumnIndex(Media.DATA)));
videoItem.size=cursor.getLong(cursor.getColumnIndex(Media.SIZE)));
videoItem.title=cursor.getString(cursor.getColumnIndex(Media.TITLE)));
return videoItem;
}
}


2.以列表的方式将视频信息显示出来

2.1 创建数据适配器

这里使用到的是ListView来设置显示数据,所以需要给ListView准备一个适配器,来设置显示数据,由于我们查询到的数据是封装在Cursor中的,所以我们这里可以创建继承于CursorAdapter的适配器

public class VideoListAdapter extends CursorAdapter{

public VideoListAdapter(Context context, Cursor c) {
super(context, c);
}

/**
* 直接从布局中加载view返回,这里就是ListView中显示的item布局,可以自定义随便布局
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return View.inflate(context, R.layout.adapter_video_list, null);
}

ViewHolder holder;
/**
* 将数据设置给view
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
holder = getHolder(view);
//获取数据,这里的数据是保存在cursor中,通过方法fromCursor直接转换成VideoItem对象保存的信息
VideoItem videoItem = VideoItem.fromCursor(cursor);

holder.tv_title.setText(videoItem.title);
//这里获取到的时长是毫秒,可以通过 自定义的时间格式来对时间进行格式化操作
holder.tv_duration.setText(StringUtil.formatVideoDuration(videoItem.duration()));
//将视频的大小格式化为M来进行数据显示
holder.tv_size.setText(Formatter.formatFileSize(context, videoItem.size()));

}

private ViewHolder getHolder(View view){
ViewHolder viewHolder = (ViewHolder) view.getTag();
if(viewHolder==null){
viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
}
return viewHolder;
}

class ViewHolder{
TextView tv_title,tv_duration,tv_size;
public ViewHolder(View view){
tv_title = (TextView) view.findViewById(R.id.tv_title);
tv_duration = (TextView) view.findViewById(R.id.tv_duration);
tv_size = (TextView) view.findViewById(R.id.tv_size);
}
}
}


2.2 为ListView设置显示数据

VideoListAdapter  adapter = new VideoListAdapter(getActivity(), null);
listView.setAdapter(adapter);


注:到这里,我们就 可以将手机中的视频的信息显示到一个ListView中了

3.点击信息条目,跳转到视频播放页面

3.1 为ListView设置条目的点击事件,并获取到相应位置的视频信息

listView.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

//获取当前点击的条目对应的数据
Cursor cursor = (Cursor) adapter.getItem(position);
Bundle bundle = new Bundle();
//当前点击播放视频的位置
bundle.putInt("currentPosition", position);
//视频信息的集合
bundle.putSerializable("videoList", cursorToList(cursor));

Intent intent = new Intent(getActivity(),VitamioPlayerActivity.class);

intent.putExtras(bundle);

getActivity().startActivity(intent);

}
});


其中调用了一个方法,是将cursor中的数据转换成集合中保存对象的方法保存信息,以方便数据的传递

private ArrayList<VideoItem> cursorToList(Cursor cursor){
ArrayList<VideoItem> list = new ArrayList<VideoItem>();
cursor.moveToPosition(-1);
while(cursor.moveToNext()){
list.add(VideoItem.fromCursor(cursor));
}
return list;
}


其中调用的VideoItem.fromCursor方法是对象中封装的转换cursor对象数据的方法

4.视频播放页面

4.1 获取传递过来的视频信息

private int currentPosition;//当前播放视频的位置
private ArrayList<VideoItem> videoList;//当前的视频列表

currentPosition = getIntent().getExtras().getInt("currentPosition");
videoList = (ArrayList<VideoItem>) getIntent().getExtras().getSerializable("videoList");


4.2 设置视频播放并设置监听

这里使用到的是控件VideoView,

//设置播放的数据
playVideo();
//设置进行播放的操作监听
video_view.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//进行视频的播放
video_view.start();
//更新视频播放指示的进度条
updatePlayProgress();
//设置指示的进度条的最大值
video_seekbar.setMax((int) video_view.getDuration());
//设置显示当前的播放时间
tv_current_position.setText("00:00");
//设置显示视频的总时长
tv_duration.setText(StringUtil.formatVideoDuration(video_view.getDuration()));
//设置播放按钮的背景图片
btn_play.setBackgroundResource(R.drawable.selector_btn_pause);
}
});
调用的方法playVideo

private void playVideo(){
//上一视频的控制按钮(如果是第一节,则不再点击)
btn_pre.setEnabled(currentPosition!=0);
//下一视频的控制按钮(如果是最后一节,则不再点击)
btn_next.setEnabled(currentPosition!=(videoList.size()-1));
//获取当前位置对应的数据
VideoItem videoItem = videoList.get(currentPosition);
//设置页面显示的视频标题
tv_name.setText(videoItem.getTitle());
//进行视频的播放
video_view.setVideoURI(Uri.parse(videoItem.getPath()));
}


playVideo方法为videoview设置了播放的数据源,当videoView加载准备好后,由于设置了监听OnPreparedListener方法,所以会调用其中的onPrepared,在这里进行开始播放视频的操作,并可以获取到当前要播放的视频的总时长,以设置指示进度的进度条显示的最大值,以及更新其他相关的UI等

4.3 设置点击播放上一节视频的方法

btn_pre.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if(currentPosition>0){
currentPosition - -;
playVideo();
}

}
});
btn_pre 是播放上一节视频的触发按钮

这里的currentPosition是当前播放的视频在视频集合中的位置,调用playVideo,会将对应位置的视频信息获取出来,然后设置给VideoView,然后当资源准备完成后,会调用到监听OnPreparedListener中的onPrepared方法,来进行相应的视频播放

4.4 设置点击播放下一节视频的方法

btn_next.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if(currentPosition<(videoList.size()-1)){
currentPosition ++;
playVideo();
}
}

}
});
btn_next是播放下一节视频的触发按钮,播放原理逻辑与播放上一节视频内容是一致的

4.5 为播放视频设置其他的相监听

//当视频播放完成时进行的操作监听
video_view.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//例如可以更新播放进度的指示大小,以及其他相关的UI操作

}
});
//视频缓冲进度的监听
video_view.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
//percent:0-100
long bufferProgress = (long) ((video_view.getDuration()/100f)*percent);
//这里设置显示视频缓冲进度条的显示   video_seekbar 是指示视频播放的进度条
video_seekbar.setSecondaryProgress((int) bufferProgress);
}
});
//视频播放卡顿时进行的操作
video_view.setOnInfoListener(new OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START :
//开始卡顿  显示缓冲加载的进度条
ll_buffer.setVisibility(View.VISIBLE);
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_END :
//卡顿结束  隐藏缓冲加载的进度条
ll_buffer.setVisibility(View.GONE);
break;
}
return true;
}
});
//视频播放错误的监听
video_view.setOnErrorListener(new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_ERROR_UNKNOWN :
//未知格式,视频文件错误,进行相关信息的提示
Toast.makeText(VitamioPlayerActivity.this, "视频格式不支持", 0).show();

break;
}
return true;
}
});
}


4.6 对播放中指示播放进度的进度条(video_seekbar)的操作

4.6.1 指示缓存进度的操作

在setOnBufferingUpdateListener这个监听中操作了video_seekbar的缓存进行更新

4.6.2 实时更新播放的指示进度

private void updatePlayProgress(){
//显示当前的播放进度
tv_current_position.setText(StringUtil.formatVideoDuration(video_view.getCurrentPosition()));
//设置进度条的指示位置
video_seekbar.setProgress((int) video_view.getCurrentPosition());
handler.sendEmptyMessageDelayed(1, 500);
}
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
updatePlayProgress();
break;

};
};


可以看到,这里使用的是handler消息机制,循环发送消息

4.6.3 手动拖动进度条控制视频播放的进度

video_seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {

}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(fromUser){
//fromUser =true;表示是用户手动拖动
//播放相应位置的视频
video_view.seekTo(progress);
//更新播放时间进度显示
tv_current_position.setText(StringUtil.formatVideoDuration(progress));
}
}
});


4.7 对播放中指示播放音量的进度条操作

4.7.1 初始化显示系统的音量

private AudioManager audioManager;
private int currentVolume;//系统音乐和视频类型当前的音量
private boolean isMute = false;//是否是静音模式
private int maxVolume;//系统中音乐和视频类型最大音量
private SeekBar voice_seekbar;//显示音量指示信息的进度条

private void initVolume(){
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//maxVolume:0-15
maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
//设置音量指示进度条的最大指示进度
voice_seekbar.setMax(maxVolume);
//设置当前的音量大小对应的指示进度显示
voice_seekbar.setProgress(currentVolume);
}


voice_seedbar 是视频页面指示播放音量大小的进度条

4.7.2 手动拖动进度条改变播放音量的大小

与拖动指示播放进度的进度条的方法一致

voice_seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {

}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(fromUser){//表示是用户手动拖动
isMute = false;
//记录当前的音量
currentVolume = progress;
//更新系统音量的操作
updateSystemVolume();
}
}
});


/**
* 更新系统音量
*/
private void updateSystemVolume(){
if(isMute){
//静音模式,设置音量大小为0,并将指示音量大小的进度条进度设置为0
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
voice_seekbar.setProgress(0);
}else {
//设置当前改变到的音量大小的进度
voice_seekbar.setProgress(currentVolume);
//第三个参数如果是1,会显示音量改变的浮动面板
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0);
}
}


4.7.3 手动上下滑动屏幕改变播放音量的大小

涉及到手势操作,做一些初始化操作准备

int  touchSlop = ViewConfiguration.getTouchSlop();

GestureDetector gestureDetector = new GestureDetector(this,new MyOnGestureListner());


可以在相关方法中做一些滑动过程中的操作

private class MyOnGestureListner extends SimpleOnGestureListener{
@Override
public void onLongPress(MotionEvent e) {
super.onLongPress(e);

}

@Override
public boolean onDoubleTap(MotionEvent e) {

return super.onDoubleTap(e);
}

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {

return super.onSingleTapConfirmed(e);
}

}


捕捉触摸事件,并将事件传递给gestureDetector;

private float downY;
@Override
public boolean onTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float moveY = event.getY();
float moveDistance = moveY - downY;

//对滑动距离进行一个值的限制
if(Math.abs(moveDistance)<touchSlop)break;

isMute = false;

float totalDistance = Math.min(screenHeight, screenWidth);
float movePercent = Math.abs(moveDistance)/totalDistance;

int moveVolume = (int) (movePercent*maxVolume);//这个值一定是0

if(moveDistance>0){
//减小音量
currentVolume -= 1;
}else {
//增大音量
currentVolume += 1;
}
updateSystemVolume();

downY = moveY;
break;
case MotionEvent.ACTION_UP:

break;
}
return super.onTouchEvent(event);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: