播放视频的数据源一般有两个,一个是请求网络,从服务器后台直接获取播放的视频信息,另一种是播放手机中本地的视频,这里我们采用的播放源为播放手机本地的视频
定义要查询到的视频的信息,包括视频的名称,视频大小,视频播放时长以及在手机中的储存位置
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);
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 } } }
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; } }
这里使用到的是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); } } }
VideoListAdapter adapter = new VideoListAdapter(getActivity(), null); listView.setAdapter(adapter);
注:到这里,我们就 可以将手机中的视频的信息显示到一个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); } });
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; }
private int currentPosition;//当前播放视频的位置 private ArrayList<VideoItem> videoList;//当前的视频列表 currentPosition = getIntent().getExtras().getInt("currentPosition"); videoList = (ArrayList<VideoItem>) getIntent().getExtras().getSerializable("videoList");
这里使用到的是控件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())); }
btn_pre.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(currentPosition>0){ currentPosition - -; playVideo(); } } });btn_pre 是播放上一节视频的触发按钮
这里的currentPosition是当前播放的视频在视频集合中的位置,调用playVideo,会将对应位置的视频信息获取出来,然后设置给VideoView,然后当资源准备完成后,会调用到监听OnPreparedListener中的onPrepared方法,来进行相应的视频播放
btn_next.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(currentPosition<(videoList.size()-1)){ currentPosition ++; playVideo(); } } } });btn_next是播放下一节视频的触发按钮,播放原理逻辑与播放上一节视频内容是一致的
//当视频播放完成时进行的操作监听 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; } }); }
在setOnBufferingUpdateListener这个监听中操作了video_seekbar的缓存进行更新
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; }; };
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)); } } });
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_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); } }
涉及到手势操作,做一些初始化操作准备
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); } }
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); }