自定義LayoutManager實現抖音的效果

抖音.gif

時下最火的莫過抖音了,實現這個效果應該很簡單嘛,用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));
            }

        }
    };
}

更加詳細的看源碼

Github地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 簡介: 提供一個讓有限的窗口變成一個大數據集的靈活視圖。 術語表: Adapter:RecyclerView的子類...
    酷泡泡閱讀 5,212評論 0 16
  • 引言:使用RecycleView實現Item滑動的自動反彈,有點類似于ViewPager那樣的滑動體驗。時間:20...
    JustDo23閱讀 3,580評論 5 9
  • 【Android 控件 RecyclerView】 概述 RecyclerView是什么 從Android 5.0...
    Rtia閱讀 307,765評論 27 439
  • 【聲明】本站文章均為宋沅君原創,請不要私自轉載或使用。有事請留言,私信。 作者:宋沅君 2017年2月的網絡及熒屏...
    宋沅君閱讀 550評論 1 2
  • 浪仙 自從被雪翠媽咪砸了以后,鄭小毛好像茅塞頓開了。我不再關注那些虛無縹緲的事情,也不再把時間花在后知后覺上了,而...
    鄭小毛閱讀 889評論 0 1