Android 不得不说的VideoView的一些坑及其解决方案
最近公司做动态展示新添加了视频,然后去摸索了一些视频的相关问题,最终选择了Android原生的VideoView。开发中遇到了一些坑给大家分享出来,不得不说很坑,希望给大家做视频播放做一个参考:先总结如下:
VideoView的问题及其解决方案:
1.视频播放时会有短暂的黑屏时间:
产生原因:视频文件加载到内存中是需要时间,这个时间可能导入VideoView全黑。
解决方法(1):给VideoView添加MediaPlayer.OnPreparedListener 监听事件,在其onPrepared(MediaPlayer mp) 方法回调中播放视频这个时候视频已经完成了加载。(PS:此方法在有些情况下使用有些问题,因为播放视频的时候MediaPlayer.OnPreparedListener 监听不到回调,要先用此方法解决最本质问题得好好研究一下源码)。
解决方法(2):笨方法,同时也是最直接的方法。在VideoView执行start() 方法时视频的预览图不是立即消失而是延迟几百毫秒之后消失,这是视频已经加载完成,当然体验上有些不好。
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(200);
runOnUiThread(new Runnable() {
@Override
public void run() {
//执行让预览图消失的方法
}
});
}
}).start();
2.进入有VideoView界面的Activity时会出现闪黑屏的情况(如论视频是否播放):
产生原因:不祥(本人认为好像是窗口的问题)
解决方法:在整个界面创建之前添加这行代码getWindow().setFormat(PixelFormat.TRANSLUCENT);
(PixelFormat.TRANSPARENT和PixelFormat.TRANSLUCENT作用差不多,都是使窗口支持透明度)
3.当前界面有视频播放时进入其他界面(或者谈起PopupWindow/分享到微信、朋朋友圈、QQ等),然后跳转回来后VideoView展示全黑(按home键再次进入app也会有同样的问题):
产生原因:VideoView被回收掉,而自己没做VideoView的状态保存处理
解决方法:在VideoView所在的Activity或者Fragment的生命周期中处理VideoView视频播放和暂停。(查看VideoView的生命周期)
4.在类似微信列表页视频播放点击大图播放时列表的VideoView回出现在大图的VideoView之上(PS:如果列表的VideoView和大图播放的VideoView不是同一个):
产生原因:SurfaceView默认会出现在最顶部的。
解决方法: 小图播放时要隐藏掉(GONE而不是INVISIBLE)。
5.VideoView嵌套ViewPager使用时,在滑动ViewPager过程中视VideoView会出现透明(此时VideoView是自动播放)(PS:此时Activity的主题为android:theme=”@style/Transparent”)。
产生原因:ViewPager在执行public void onPageSelected(int position) 方法时当前界面还是展示两个View。即下一个View并没有完全漏出来。
解决方法:定义ViewPager的ViewPager.OnPageChangeListener接口,覆盖public void onPageScrolled(int postion, float v, int i)方法,监听让下一个界面完全展现出来之后在执行public void onPageSelected(int position)方法。然后再在方法里处理下一个视频的播放以及上一个的停止。
6.使用VideoView造成界面其他组价焦点产生“遗失”:
现象:在ViewTreeObserver.addOnPreDrawListener添加新的OnPreDrawListener的onPreDraw多次执行。
解决办法(1):使用SurfaceView+MediaPlayer自定义播放器
解决办法(2):对VideoView的构造函数中的焦点处理的方法进行反处理,即在VideoView子类(这里应该就是自己写的自定义VideoView其继承SurfaceView)的构造方法中进行反处理。
自定义的VideoView
public class VideoView extends SurfaceView
implements MediaPlayerControl, SubtitleController.Anchor {
private String TAG = "VideoView";
/*其他代码省略*/
//每个构造函数中都执行initVideoView
public VideoView(Context context) {
super(context);
initVideoView();
}
public VideoView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
initVideoView();
}
public VideoView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public VideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initVideoView();
}
/*其他代码省略*/
private void initVideoView() {
mVideoWidth = 0;
mVideoHeight = 0;
getHolder().addCallback(mSHCallback);
getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//start 下边3行代码是对焦点进行的处理,以及自己所谓的反处理
setFocusable(true); //子类setFocusable(false);
setFocusableInTouchMode(true); //子类setFocusableInTouchMode(false);
requestFocus(); //子类clearFocus();
//end
mPendingSubtitleTracks = new Vector>();
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
}
/*其他代码省略*/
}
7.在VideoView的MediaPlayer.OnCompletionListener 回调监听public void onCompletion(MediaPlayer mp) 方法里进行视频的播放(PS:这个指的是视频的循环播放),在有些手机上不能正常重新播放。
产生原因:在部分手机上VideoView的MediaPlayer.OnCompletionListener 回调监听public void onCompletion(MediaPlayer mp) 方法里此时`VideoView.isPlaying() 的值还是为true。
解决办法:在VideoView的 MediaPlayer.OnCompletionListener 回调监听 public void onCompletion(MediaPlayer mp) 方法里对`VideoView.start() 方法进行延迟播放。一般这个时间不会太长,几百毫秒就可以。视觉感观上也还好。
8.手机分辨率导致的无法播放此视频的问题
产生原因:视频的分辨率与手机分辨率的问题
解决办法:设置surfaceView的布局参数
//首先取得video的宽和高
int vWidth = player.getVideoWidth();
int vHeight = player.getVideoHeight();
if (vWidth > video_view.getWidth() || vHeight > video_view.getHeight()) {
//如果video的宽或者高超出了当前屏幕的大小,则要进行缩放
float wRatio = (float) vWidth / (float) video_view.getWidth();
float hRatio = (float) vHeight / (float) video_view.getHeight();
//选择大的一个进行缩放
float ratio = Math.max(wRatio, hRatio);
vWidth = (int) Math.ceil((float) vWidth / ratio);
vHeight = (int) Math.ceil((float) vHeight / ratio);
//设置surfaceView的布局参数
video_view.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight));
//然后开始播放视频
player.start();
}
这个写在setOnPreparedListener的onPrepared()方法中去执行