時下最火的莫過抖音了,實現這個效果應該很簡單嘛,用ViewPager就可以了。但是等你通過ViewPager來實現的時候,手機內存不夠用的情況就會顯現出來。有沒有更好的方式呢???自然是有,每個人都會用RecyclerView吧,我們就用RecyclerView來實現這個效果,關于內存的回收利用就交給RecyclerView就好了。
那么我們怎么通過RecyclerView來實現這個效果呢?如果你使用過SnapHelper的話,就會很好理解。
實戰抖音
1.自定義LayoutManager,并且繼承LinearLayoutManager,這樣就得到一個可以水平排向或者豎向排向的布局策略。如果你接觸過SnapHelper應該了解一個LinearSnapHelper的類,可以實現讓列表的Item居中顯示的效果。但是這里我們不用這個類,我們要的效果是一次只能滑動一個Item,也就是一頁。PagerSnapHelper就可以做到哦,
@Override
public void onAttachedToWindow(RecyclerView view) {
super.onAttachedToWindow(view);
mPagerSnapHelper.attachToRecyclerView(view);
this.mRecyclerView = view;
}
2.經過第一步基本可以實現抖音的效果,但是寫完之后一會發現,不知道哪里來開始播放視頻和在哪里釋放視頻。不要著急,要監聽滑動到哪頁,需要我們重寫onScrollStateChanged()函數,這里面有三種狀態:SCROLL_STATE_IDLE(空閑),SCROLL_STATE_DRAGGING(拖動),
SCROLL_STATE_SETTLING(要移動到最后位置時)。我們需要的就是RecyclerView停止時的狀態,我們就可以拿到這個View的Position.注意這里還有一個問題,當你通過這個position去拿Item會報錯,這里涉及到RecyclerView的緩存機制,自己去腦補~~。打印Log,你會發現RecyclerView.getChildCount()一直為1或者會出現為2的情況。好了,我們自己來實現一個接口然后通過接口把狀態傳遞出去。
監聽器
/**
* Created by 釘某人
* github: https://github.com/DingMouRen
* email: naildingmouren@gmail.com
* 用于ViewPagerLayoutManager的監聽
*/
public interface OnViewPagerListener {
/*初始化完成*/
void onInitComplete();
/*釋放的監聽*/
void onPageRelease(boolean isNext,int position);
/*選中的監聽以及判斷是否滑動到底部*/
void onPageSelected(int position,boolean isBottom);
}
獲取到RecyclerView空閑時選中的Item,重寫LinearLayoutManager的onScrollStateChanged方法
@Override
public void onScrollStateChanged(int state) {
switch (state) {
case RecyclerView.SCROLL_STATE_IDLE:
View viewIdle = mPagerSnapHelper.findSnapView(this);
int positionIdle = getPosition(viewIdle);
if (mOnViewPagerListener != null && getChildCount() == 1) {
mOnViewPagerListener.onPageSelected(positionIdle,positionIdle == getItemCount() - 1);
}
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
View viewDrag = mPagerSnapHelper.findSnapView(this);
int positionDrag = getPosition(viewDrag);
break;
case RecyclerView.SCROLL_STATE_SETTLING:
View viewSettling = mPagerSnapHelper.findSnapView(this);
int positionSettling = getPosition(viewSettling);
break;
}
}
3.列表的選中監聽好了,我們就看看什么時候釋放視頻的資源,第二步中的三種狀態,去打印getChildCount()的日志,你會發現getChildCount()在SCROLL_STATE_DRAGGING會為1,SCROLL_STATE_SETTLING為2,SCROLL_STATE_IDLE有時為1,有時為2,還是RecyclerView的緩存機制O(∩∩)O,這里不會去贅述緩存機制,我們要做的是要知道在什么時候去做釋放視頻的操作,還要分清是釋放上一頁還是下一頁,因為適配器adapter的position在這里不好使嘛,O(∩∩)O,這里有兩個方法scrollHorizontallyBy()和scrollVerticallyBy()可以拿到滑動偏移量,可以判斷滑動方向,好~ 齊活了。O(∩_∩)O,看代碼
/**
* 監聽豎直方向的相對偏移量
* @param dy
* @param recycler
* @param state
* @return
*/
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
this.mDrift = dy;
return super.scrollVerticallyBy(dy, recycler, state);
}
/**
* 監聽水平方向的相對偏移量
* @param dx
* @param recycler
* @param state
* @return
*/
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
this.mDrift = dx;
return super.scrollHorizontallyBy(dx, recycler, state);
}
private RecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener = new RecyclerView.OnChildAttachStateChangeListener() {
//第一次進入界面的監聽,可以用來實現首次播放的邏輯
@Override
public void onChildViewAttachedToWindow(View view) {
if (mOnViewPagerListener != null && getChildCount() == 1) {
mOnViewPagerListener.onInitComplete();
}
}
// 可以釋放資源的監聽,也就是回收Item的時候
@Override
public void onChildViewDetachedFromWindow(View view) {
if (mDrift >= 0){
if (mOnViewPagerListener != null) mOnViewPagerListener.onPageRelease(true,getPosition(view));
}else {
if (mOnViewPagerListener != null) mOnViewPagerListener.onPageRelease(false,getPosition(view));
}
}
};
大功告成。全部源碼
/**
* Created by 釘某人
* github: https://github.com/DingMouRen
* email: naildingmouren@gmail.com
* 用于ViewPagerLayoutManager的監聽
*/
public interface OnViewPagerListener {
/*初始化完成*/
void onInitComplete();
/*釋放的監聽*/
void onPageRelease(boolean isNext,int position);
/*選中的監聽以及判斷是否滑動到底部*/
void onPageSelected(int position,boolean isBottom);
}
**
* Created by 釘某人
* github: https://github.com/DingMouRen
* email: naildingmouren@gmail.com
*/
public class ViewPagerLayoutManager extends LinearLayoutManager {
private static final String TAG = "ViewPagerLayoutManager";
private PagerSnapHelper mPagerSnapHelper;
private OnViewPagerListener mOnViewPagerListener;
private RecyclerView mRecyclerView;
private int mDrift;//位移,用來判斷移動方向
public ViewPagerLayoutManager(Context context, int orientation) {
super(context, orientation, false);
init();
}
public ViewPagerLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
init();
}
private void init() {
mPagerSnapHelper = new PagerSnapHelper();
}
@Override
public void onAttachedToWindow(RecyclerView view) {
super.onAttachedToWindow(view);
mPagerSnapHelper.attachToRecyclerView(view);
this.mRecyclerView = view;
mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);
}
/**
* 滑動狀態的改變
* 緩慢拖拽-> SCROLL_STATE_DRAGGING
* 快速滾動-> SCROLL_STATE_SETTLING
* 空閑狀態-> SCROLL_STATE_IDLE
* @param state
*/
@Override
public void onScrollStateChanged(int state) {
switch (state) {
case RecyclerView.SCROLL_STATE_IDLE:
View viewIdle = mPagerSnapHelper.findSnapView(this);
int positionIdle = getPosition(viewIdle);
if (mOnViewPagerListener != null && getChildCount() == 1) {
mOnViewPagerListener.onPageSelected(positionIdle,positionIdle == getItemCount() - 1);
}
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
View viewDrag = mPagerSnapHelper.findSnapView(this);
int positionDrag = getPosition(viewDrag);
break;
case RecyclerView.SCROLL_STATE_SETTLING:
View viewSettling = mPagerSnapHelper.findSnapView(this);
int positionSettling = getPosition(viewSettling);
break;
}
}
/**
* 布局完成后調用
* @param state
*/
@Override
public void onLayoutCompleted(RecyclerView.State state) {
super.onLayoutCompleted(state);
if (mOnViewPagerListener != null) mOnViewPagerListener.onLayoutComplete();
}
/**
* 監聽豎直方向的相對偏移量
* @param dy
* @param recycler
* @param state
* @return
*/
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
this.mDrift = dy;
return super.scrollVerticallyBy(dy, recycler, state);
}
/**
* 監聽水平方向的相對偏移量
* @param dx
* @param recycler
* @param state
* @return
*/
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
this.mDrift = dx;
return super.scrollHorizontallyBy(dx, recycler, state);
}
/**
* 設置監聽
* @param listener
*/
public void setOnViewPagerListener(OnViewPagerListener listener){
this.mOnViewPagerListener = listener;
}
private RecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener = new RecyclerView.OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(View view) {
if (mOnViewPagerListener != null && getChildCount() == 1) {
mOnViewPagerListener.onInitComplete();
}
}
@Override
public void onChildViewDetachedFromWindow(View view) {
if (mDrift >= 0){
if (mOnViewPagerListener != null) mOnViewPagerListener.onPageRelease(true,getPosition(view));
}else {
if (mOnViewPagerListener != null) mOnViewPagerListener.onPageRelease(false,getPosition(view));
}
}
};
}
更加詳細的看源碼