看下項目整體實現效果,上下翻頁時完成視頻切換:
視頻的切換這裏我們使用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);
}
}