项目需要做一个简单的播放视频功能demo,考虑到需求较简单(实现不超过2min的MP4格式短视频播放),所以就没考虑使用复杂的第三方视频库了。最初敲定使用系统原生的VideoView实现,后来发现不能在列表中使用普通的VideoView 。VideoView 继承自SurfaceView,而SurfaceView并没有UI同步缓冲区。这就导致了在列表滚动的时候,正在播放的视频可能无法跟上滚动的步伐。针对这个问题继续查资料,最终选择使用MediaPlayer+TextureView实现视频播放。相关的资料很多,这里就不细说了。我主要说一下实现全屏播放的实现思路及遇到的问题。
一、在activity中留一个全屏的ViewGroup放置播放器实现全屏
在相应Activity中预留一个放置播放器的宽高都是match_parent的ViewGroup,大小切换就是把播放器添加到本来的小容器和添加到全屏的ViewGroup中来回切换。
全屏时,把从当前视图移除的播放器View即自定义的TextureView重新添加到预留的ViewGroup中,并且设置activity为横屏。
在activity中监听屏幕状态变化:
@Override
public void onConfigurationChanged(Configuration newConfig) {
TestLog.d(TAG, "onConfigurationChanged : fullScreen=" + fullScreen);
super.onConfigurationChanged(newConfig);
if (fullScreen != null && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && getVideoContent() != null) {
getVideoContent().setClickable(true);//拦截点击事件
ViewUtils.setVisible(getVideoContent(), View.VISIBLE);
fullScreen.full();
fullScreen = null;
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
ViewUtils.setVisible(getVideoContent(), View.GONE);
}
}
相应的实现点击全屏按钮实现全屏逻辑:
public void clickESC() {
PingbackUtils_3_9_0.pingVideoFullScreen();
if (mAdapter != null) {
final Context context = mAdapter.getContext();
if (context instanceof BaseActivity) {
((BaseActivity) context).setFullScreen(new IFullScreen() {
@Override
public void full() {
handler.addVideo((ViewGroup) ((BaseActivity) context).getRootView(), false);
TestLog.d(TAG,
TestLog.isDebug() ? "clickESC videoInfo.getCurrentPosition()=" + videoInfo.getCurrentPosition() : "");
//handler.seekTo(videoInfo.getCurrentPosition());
handler.fullScreen((BaseActivity) context);//这个要放到addVideo后面,需要是否全屏的状态
}
});
SingleVideoHandler.setOrientation((BaseActivity) context);
}
}
}
退出全屏倒着做就好了,设置会竖屏,将
播放器view
从
ViewGroup
中移除,重新添加到当前视图。
注意:切换横竖屏时为了避免Activity
重新走生命周期,需要在Manifest.xml
的activity
标签下添加如下配置:
android:configChanges="orientation|keyboardHidden|screenSize"
到了这里别以为已经万事大吉了,亲自实践发现小屏播放和切换大屏衔接的不好,会出现短暂的黑屏现象。这是因为切换到大屏
时视频view从当前播放视图移除重新添加到大屏视图后,TextureView会重绘,就会重新new一个surfaceTexture,并重新回调onSurfaceTextureAvailable()
方法,这样TextureView的数据通道SurfaceTexture发生了变化,但是mediaPlayer还是持有原来的SurfaceTexture。
解决方法:重写onSurfaceTextureAvailable()、onSurfaceTextureDestroyed(),在从当前视图移除播放器viwe之前保存当前SurfaceTexture,
重新添加到大屏视图时,将之前保存的SurfaceTexture重新设置给当前播放器view。
@Override
public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) {
TestLog.d(TAG, TestLog.isDebug() ? "onSurfaceTextureAvailable:surface=" + surface : "");
TestLog.d(TAG, TestLog.isDebug() ? "onSurfaceTextureAvailable:mSurfaceTexture=" + mSurfaceTexture : "");
if (Utils.hasJellyBeanMR2()) {//如下方法在android4.2会崩溃,所以4.3之后(包括4.3)再使用
if (mSurfaceTexture == null) {
mSurfaceTexture = surface;
TestLog.d(TAG, TestLog.isDebug() ? "after set:mSurfaceTexture=" + mSurfaceTexture : "");
openVideo();
} else {
setSurfaceTexture(mSurfaceTexture);
}
} else {
mSurface = new Surface(surface);
openVideo();
}
}
@Override
public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) {
TestLog.d(TAG, TestLog.isDebug() ? "onSurfaceTextureDestroyed:surface=" + surface : "");
// after we return from this we can't use the surface any more
/*if (mSurface != null) {
mSurface.release();
mSurface = null;
}
if (mMediaController != null) mMediaController.hide();
release(true);
return true;*/
return mSurfaceTexture == null;
}
参考:
VideoView and TextureVideoView on Android