http://blog.csdn.net/u010181592/article/category/5893483
先列出参考资料:
- Vitamio 官网:http://www.vitamio.org(不太稳定,时常打不开)
- 农民伯伯 博客:http://www.cnblogs.com/over140/category/409230.html(开发者之一,博客中有部分Vitamio中文API)
2017/03/28更新:
csdn都能遇到键盘侠,真是林子大了什么鸟都有~介于遇到键盘侠,删除所有相关demo资源链接,demo查看 请移步github。
半年没更新博客,解决了学校的麻烦事终于毕业了,回来填坑了。 根据之前的博客,基于Vitamio的播放器已经差不多了,这次添加最后一个基本功能,左右滑动 快进快退;
因为vitamio在6月份更新了,版本升级到5.2.0 据说是增加了一些机型支持修复了一些bug。我们也使用下最新的vitamio库。导入跟以前的都一样,唯一区别是引用, 旧版本会在 setContentView() 之前添加一个解码器判断,新版本则不用 ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!io.vov.vitamio.LibsChecker.checkVitamioLibs(this))
return;
setContentView(R.layout.activity_play);
}
这里是旧版本的 构造函数
if (!io.vov.vitamio.LibsChecker.checkVitamioLibs(this))return;
新版本则把解析包方法放到了自定义的application中 ,并且改为:
Vitamio.isInitialized(getApplicationContext());
剩下的和以前就没太大的区别;
回归正题 关于在播放时左右滑动来控制进度的问题,思路是 在播放界面监听手指的动作 ——>然后获取到滑动距离,根据比例换算为视频进度 ——> 加载对应进度
思路确定,当左右快进快退时, 还需要一个提示窗口和下边进度条的配合,提示窗可以复用声音和亮度的提示框。剩下就是和进度条结合了。
关于进度条,原来的控制器是继承官方提供的 MediaController ,进度条官方的控制器封装的很死,跟进度条有关得方法和变量都是私有,这里大概列一下
尤其是seekbar的监听 ,我们如果想在外边操作,要么完全重新写seekbar相关方法和属性,要么我们自己把seekbar给暴露出来,评估了一下工作量,博主只好选择 在官方的MediaController里边 手动暴露出 4个方法 ,用来操作seekbar 他们分别是:
/** 获取进度条进度
* @return
*/
public int getProgress() {
if (mProgress != null)
return mProgress.getProgress();
return 0;
}
/**改变进度条进度
* @param progress
* @return
*/
public String setSeekBarChange(int progress) {
if (mProgress == null) return "";
mProgress.setProgress(progress);
long newposition = (mDuration * progress) / 1000;
String time = StringUtils.generateTime(newposition);
if (mInstantSeeking)
mPlayer.seekTo(newposition);
if (mInfoView != null)
mInfoView.setText(time);
if (mCurrentTime != null)
mCurrentTime.setText(time);
return time;
}
/**
* 进度条开始改变
*/
public void onStartSeekBar() {
if (mAM == null) return;
mDragging = true;
show(3600000);
mHandler.removeMessages(SHOW_PROGRESS);
if (mInstantSeeking)
mAM.setStreamMute(AudioManager.STREAM_MUSIC, true);
if (mInfoView != null) {
mInfoView.setText("");
}
}
/**
* 进度条变化停止
*/
public void onFinishSeekBar() {
if (mProgress == null) return;
if (!mInstantSeeking)
mPlayer.seekTo((mDuration * mProgress.getProgress()) / 1000);
System.out.println("MediaController-" + (mDuration * mProgress.getProgress()) / 1000);
System.out.println("MediaController-" + mProgress.getProgress());
if (mInfoView != null) {
mInfoView.setText("");
mInfoView.setVisibility(View.GONE);
}
show(sDefaultTimeout);
mHandler.removeMessages(SHOW_PROGRESS);
mAM.setStreamMute(AudioManager.STREAM_MUSIC, false);
mDragging = false;
mHandler.sendEmptyMessageDelayed(SHOW_PROGRESS, 1000);
}
可以看出 后边3个方法就是直接把seekbar 的监听方法暴露出来 用来在外边直接操作seekbar;
剩下的就是在 我们自己的controller中实现我们的思路;
首先监听触摸事件 在GestureDetector.SimpleOnGestureListener 的 ondown () 方法中 我们要保存一次开始时进度条的位置;
@Override
public boolean onDown(MotionEvent e) {
progress = getProgress();
return true;
}
然后 onScroll中判断手指位移
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
float beginX = e1.getX();
float endX = e2.getX();
float beginY = e1.getY();
float endY = e2.getY();
Display disp = activity.getWindowManager().getDefaultDisplay();
Point size = new Point();
disp.getSize(size);
int windowWidth = size.x;
int windowHeight = size.y;
if (Math.abs(endX - beginX) < Math.abs(beginY - endY)) {//上下滑动
if (beginX > windowWidth * 3.0 / 4.0) {// 右边滑动 屏幕3/5
onVolumeSlide((beginY - endY) / windowHeight);
} else if (beginX < windowWidth * 1.0 / 4.0) {// 左边滑动 屏幕2/5
onBrightnessSlide((beginY - endY) / windowHeight);
}
}else {//左右滑动
onSeekTo((endX - beginX) / 20);
}
return super.onScroll(e1, e2, distanceX, distanceY);
}
接着在 OntouchEvent中 监听手势结束的处理
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mGestureDetector.onTouchEvent(event)) return true;
// 处理手势结束
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
mVolume = -1;
mBrightness = -1f;
// 隐藏
myHandler.removeMessages(HIDEFRAM);
myHandler.sendEmptyMessageDelayed(HIDEFRAM, 1);
if (progress_turn) {
onFinishSeekBar();
progress_turn = false;
}
break;
}
return super.onTouchEvent(event);
}
这样手势响应完成,接下来关注 onseekto这个方法 传入参数 ,它负责引动seekbar
/**
* 滑动改变播放进度
*
* @param percent
*/
private void onSeekTo(float percent) {
//计算并显示 前进后退
if (!progress_turn) {
onStartSeekBar();
progress_turn = true;
}
int change = (int) (percent);
if (change > 0) {
mOperationBg.setImageResource(R.drawable.right);
} else {
mOperationBg.setImageResource(R.drawable.left);
}
mOperationTv.setVisibility(View.VISIBLE);
mVolumeBrightnessLayout.setVisibility(View.VISIBLE);
if (progress + change > 0) {
if ((progress + change < 1000))
mOperationTv.setText(setSeekBarChange(progress + change) + "/" + StringUtils.generateTime(videoView.getDuration()));
else
mOperationTv.setText(setSeekBarChange(1000) + "/" + StringUtils.generateTime(videoView.getDuration()));
} else {
mOperationTv.setText(setSeekBarChange(0) + "/" + StringUtils.generateTime(videoView.getDuration()));
}
}
这里说明下 这个demo没有对 手指移动距离做比例换算,正常情况下 根据需求的不同 手指一动距离会代表不同的值,example: 有些APP 手指滑动一个屏幕的距离,播放10分钟; 有些则是滑动距离根据视频时长的百分比跳转。但无论怎样,要在比例换算后 对progress做最大最小值限制,否则会出现显示错误
public class VideoView extends SurfaceView implements MediaController.MediaPlayerControl
用于播放视频文件。VideoView可以加载各种来源的图像(如资源或ContentProvider),注意计算视频尺寸,以便在任意布局管理器中使用,它还提供了诸如缩放等显示选项。
VideoView也提供封装了许多MediaPlayer的方法,例如getVideoWidth、setSubShown。
public static final int VIDEO_LAYOUT_ORIGIN
缩放参数,原始画面大小。
常量值:0
public static final int VIDEO_LAYOUT_SCALE
缩放参数,画面全屏。
常量值:1
public static final int VIDEO_LAYOUT_STRETCH
缩放参数,画面拉伸。
常量值:2
public static final int VIDEO_LAYOUT_ZOOM
缩放参数,画面裁剪。
常量值:3
public void setVideoLayout(int layout, float aspectRatio)
获取扫描视频的Uri。
参数
layout 缩放参数
aspectRation 宽高比,为0将自动检测。
public boolean isValid()
Surface是否有效。 参见Surface的isValid方法。
public void setVideoPath(String path)
设置视频路径。
public void setVideoURI(Uri uri)
设置视频URI。(可以是网络视频地址)
public void stopPlayback()
停止视频播放,并释放资源。
public void setMediaController(MediaController controller)
设置媒体控制器。
参数
controller 媒体控制器,注意是io.vov.vitamio.widget.MediaController。
public void setOnPreparedListener(OnPreparedListener l)
注册一个回调函数,在视频预处理完成后调用。在视频预处理完成后被调用。此时视频的宽度、高度、宽高比信息已经获取到,此时可调用seekTo让视频从指定位置开始播放。
public void setOnCompletionListener(OnCompletionListener l)
注册一个回调函数,视频播放完成后调用。
public void setOnErrorListener(OnErrorListener l)
注册一个回调函数,在异步操作调用过程中发生错误时调用。例如视频打开失败。
public void setOnBufferingUpdateListener(OnBufferingUpdateListener l)
注册一个回调函数,在网络视频流缓冲变化时调用。
public void setOnSeekCompleteListener(OnSeekCompleteListener l)
注册一个回调函数,在seek操作完成后调用。
public void setOnSubtitleUpdateListener(OnSubtitleUpdateListener l)
注册一个回调函数,在字幕需要显示时调用。
public void setOnInfoListener(OnInfoListener l)
注册一个回调函数,在有警告或错误信息时调用。例如:开始缓冲、缓冲结束、下载速度变化。
public boolean onTouchEvent(MotionEvent ev)
处理显示/隐藏MediaController。
public void start()
开始播放。
public void pause()
暂停播放。
public void suspend()
挂起(暂时没有实现功能)
public void resume()
恢复播放。
public long getDuration()
获取视频播放时长。
public long getCurrentPosition()
获取当前播放位置。
public void seekTo(long msec)
设置播放位置。
参数
msec 位置
public boolean isPlaying()
是否正在播放。
public int getBufferPercentage()
获取缓冲百分比。
public int getVideoWidth()
获取视频宽度。
public int getVideoHeight()
获取视频高度。
public float getVideoAspectRatio()
设置视频宽高比例。没有视频或者宽高不正确返回0。
public void setVideoQuality(int quality)
设置视频质量。
参数
quality 参见MediaPlayer的常量:VIDEOQUALITY_LOW(流畅)、VIDEOQUALITY_MEDIUM(普通)、VIDEOQUALITY_HIGH(高质)。
public void setBufferSize(int bufSize)
设置视频缓冲大小。默认1024KB,单位byte
public boolean isBuffering()
检测是否缓冲完毕。
public void setMetaEncoding(String encoding)
设置元数据编码。例如:UTF-8
public String getMetaEncoding()
获取元数据编码。
public HashMap getAudioTrackMap(String encoding)
获取视频中嵌入的音轨。例如:English
public int getAudioTrack()
设置播放音轨编号。
public void setAudioTrack(int audioIndex)
设置音轨编号,必须使用getAudioTrackMap的返回值。
public void setSubShown(boolean shown)
设置是否显示字幕。
参数
shown true表示显示字幕
public void setSubEncoding(String encoding)
设置字幕编码。
参数
encoding 字幕编码。如果为null将自动检测。
public int getSubLocation()
获取字幕位置类型。0为内嵌字幕,1为外挂字幕。
public void setSubPath(String subPath)
设置外挂字幕路径。必须是本地文件路径。
public String getSubPath()
获取外挂字幕路径。
public void setSubTrack(int trackId)
设置字幕编号。必须是getSubTrackMap的返回值。
public int getSubTrack()
获取字幕编号。
public HashMap getSubTrackMap(String encoding)
获取视频内嵌字幕集合。
参数
encoding 格式化字符串编码。如果为null将自动检测。
返回值
返回字幕名称和字幕编号组成的Map。
public boolean canPause()
是否可暂停。(暂时没有实现功能)
public boolean canSeekBackward()
(暂时没有实现功能)
public boolean canSeekForward()
(暂时没有实现功能)
受保护方法
protected boolean isInPlaybackState()
是否处于正在播放的状态。
参考Android中文API(125) —— VideoView
Github:WHPlayer
其他功能在之前的博客中都完善的差不多了 , 目前播放器主流的功能基本实现了。视频播放器教程基本完成。坑终于填完了。什么时候再更新,看命运了