看下项目整体实现效果,上下翻页时完成视频切换:
视频的切换这里我们使用RecyclerView进行实现,核心是需要自定义LayoutManager;视频播放我们使用VideoView控件。
在MyLayoutManager加载成功后,我们需要添加监听addOnChildAttachStateChangeListener,目的是监听到RecyclerView中一个item的滑出以及下一个item的进入,以方便我们关闭上一个滑出item的视频播放,以及开始下一个划入item的视频播放。
@Override
public void onAttachedToWindow(RecyclerView view) {
// 监听子item上一个移除,下一个进入的回调方法
// 重写:onChildViewAttachedToWindow,onChildViewDetachedFromWindow
view.addOnChildAttachStateChangeListener(this);
mPagerSnapHelper.attachToRecyclerView(view);
super.onAttachedToWindow(view);
}
//当Item添加进来了调用这个方法
@Override
public void onChildViewAttachedToWindow(@NonNull View view) {
//播放视频操作:即将要播放的是上一个视频,还是下一个视频
if (mDrift > 0) {
//向上
if (mOnViewPagerListener != null) {
mOnViewPagerListener.onPageSelected(getPosition(view), true);
}
} else {
if (mOnViewPagerListener != null) {
mOnViewPagerListener.onPageSelected(getPosition(view), false);
}
}
}
@Override
public void onChildViewDetachedFromWindow(@NonNull View view) {
//暂停播放操作
if (mDrift >= 0) {
if (mOnViewPagerListener != null)
mOnViewPagerListener.onPageRelease(true, getPosition(view));
} else {
if (mOnViewPagerListener != null)
mOnViewPagerListener.onPageRelease(false, getPosition(view));
}
}
我们监听scrollVerticallyBy方法即可判断出是上滑还是下滑,进而完成暂停上一个视频后播放下一个视频:
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
this.mDrift = dy;
return super.scrollVerticallyBy(dy, recycler, state);
}
在MainActivity的回调中实现视频播放关闭即可。
设置自定义LayoutManager的回调监听,以便MainActivity完成视频暂停、关闭、开始播放的逻辑:
设置的监听回调为:
public interface OnViewPagerListener {
/*初始化完成*/
void onInitComplete();
/*释放的监听*/
void onPageRelease(boolean isNext, int position);
/*选中的监听以及判断是否滑动到底部*/
void onPageSelected(int position, boolean isBottom);
}
接口实现:
private void initListener() {
myLayoutManager.setOnViewPagerListener(new OnViewPagerListener() {
@Override
public void onInitComplete() {
}
@Override
public void onPageRelease(boolean isNext, int position) {
Log.e(TAG, "释放位置:" + position + " 下一页:" + isNext);
int index = 0;
if (isNext) {
index = 0;
} else {
index = 1;
}
releaseVideo(index);
}
@Override
public void onPageSelected(int position, boolean isNext) {
Log.e(TAG, "释放位置:" + position + " 下一页:" + isNext);
int index = 0;
if (isNext) {
index = 0;
} else {
index = 1;
}
playVideo(index);
}
});
}
我们使用VideoView进行视频的播放控制:
private void releaseVideo(int index) {
View itemView = mRecyclerView.getChildAt(index);
final VideoView videoView = itemView.findViewById(R.id.video_view);
final ImageView imgThumb = itemView.findViewById(R.id.img_thumb);
final ImageView imgPlay = itemView.findViewById(R.id.img_play);
videoView.stopPlayback();
imgThumb.animate().alpha(1).start();
imgPlay.animate().alpha(0f).start();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void playVideo(int position) {
View itemView = mRecyclerView.getChildAt(0);
final VideoView videoView = itemView.findViewById(R.id.video_view);
final ImageView imgPlay = itemView.findViewById(R.id.img_play);
final ImageView imgThumb = itemView.findViewById(R.id.img_thumb);
final RelativeLayout rootView = itemView.findViewById(R.id.root_view);
final MediaPlayer[] mediaPlayer = new MediaPlayer[1];
videoView.start();
videoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
mediaPlayer[0] = mp;
mp.setLooping(true);
imgThumb.animate().alpha(0).setDuration(200).start();
return false;
}
});
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
}
});
imgPlay.setOnClickListener(new View.OnClickListener() {
boolean isPlaying = true;
@Override
public void onClick(View v) {
if (videoView.isPlaying()) {
imgPlay.animate().alpha(1f).start();
videoView.pause();
isPlaying = false;
} else {
imgPlay.animate().alpha(0f).start();
videoView.start();
isPlaying = true;
}
}
});
}
当我们滑动RecyclerView中的子item到一半时它是定位在那里不会继续滑动的,而viewPager是可以当一个page滑动到一多半时,松手后这个page会自动滑出来;为了让RecyclerView实现和viewPager一样的松手滑动的效果,我们还需要使用PagerSnapHelper实现滑动监听,可以看到这个类最终是继承自RecyclerView.OnFlingListener,这个类的作用就是手指猛然滑动item时,RecyclerView能够成功滑动到下一个完整的item。
还有需要注意的一点是onChildViewAttachedToWindow和onChildViewDetachedFromWindow方法并不是成对的出现,例如上面的item高度是30dp,而他下面的item高度是300dp,当上面item移除出界面时会调用detach但是下面由于过高还没有触发到attached方法,因此我们应该还需要监听RecyclerView的onScrollStateChanged方法进行是否滑动完成状态的监听,当滑动完成时,使用RecyclerView.SCROLL_STATE_IDLE这个状态进行下一个item的视频播放的触发点即可。
此处还有个小bug,使用videoView播放视频时候无法撑满屏幕,需要重写他的onMeasure方法:
public class FullScreenVideoView extends VideoView {
public FullScreenVideoView(Context context) {
super(context);
}
public FullScreenVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FullScreenVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int width = wm.getDefaultDisplay().getWidth();
int height = wm.getDefaultDisplay().getHeight();
setMeasuredDimension(width, height);
}
}