嵌套滑動通用解決方案--NestedScrollingParent2

以前寫過一篇嵌套滑動–NestedScroll-項目實例(淘寶首頁缺陷),及CoordinatorLayout 和 AppbarLayout 聯動原理,比較了淘寶和京東首頁的滑動效果,分析了效果呈現差異的緣由,給出了大體的解決方案。
當時沒有給出demo,只有代碼片斷,可能致使閱讀起來不很清晰,因此這篇就專門再來詳細分析相關知識,給出通用的嵌套滑動的解決方案,且附上GitHub的Demo。
html

本文相關代碼Demo Github地址,有幫助的話Star一波吧。java

1、問題及解決方案

先來看一張圖:
京東首頁
這是京東的首頁,忽略頂部和頂部,大體理解視圖結構就是:最外層爲多佈局的RecyclerView,最後一個item是tabLayout+ViewPager,ViewPager的每一個fragment內也是RecyclerView。這是電商App首頁經常使用的佈局方式。

android

再來看下滑動起來的效果圖:
在這裏插入圖片描述
可見,在向上滑動頁面時,當tabLayout滑動到頂部時,外層RecyclerView中止滑動,此時tabLayout即爲吸頂狀態,接着會 滑動ViewPager中的內層RecyclerView。向下滑動時,若是tabLayout是吸頂狀態,那麼會先滑動內層RecyclerView,而後再滑外層RecyclerView。

git

那麼,若是咱們 直接 按上述佈局結構來實現,會是京東這種效果嗎?答案是否認的,效果以下?github

按分析的view結構直接實現
可見,在tabLayout是吸頂狀態,沒法繼續滑動內層RecyclerView(擡起手指繼續滑也不行)。 (點擊查看相關代碼
markdown

那麼該咋辦呢?根據滑動衝突的相關知識,咱們知道必定是外層RecyclerView攔截了觸摸事件,內層RecyclerView沒法獲取事件,就沒法滑動了。那麼是否能夠在tabLayout吸頂時,外層不要攔截事件,從而內層RecyclerView獲取事件進而滑動呢?app

這是可行的,可是在tabLayout滑動到頂部後,必須擡起手指,從新滑動,內層RecyclerView才能繼續滑動。 這是爲啥呢?開頭提到的博客中有說明:ide

從view事件分發機制 咱們知道,當parent View攔截事件後,那同一事件序列的事件會直接都給parent處理,子view不會接受事件了。因此按照正常處理滑動衝突的思路處理–當tab沒到頂部時,parent攔截事件,tab到頂部時 parent就不攔截事件,可是因爲手指沒擡起來,因此這一事件序列仍是繼續給parent,不會到內部RecyclerView,因此商品流就不會滑動了。函數

解決方案只能是嵌套滑動佈局了。代碼以下:佈局

<?xml version="1.0" encoding="utf-8"?>
<com.hfy.demo01.module.home.touchevent.view.NestedScrollingParent2LayoutImpl3 xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/nested_scrolling_parent2_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView_parent"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</com.hfy.demo01.module.home.toucheve

看到咱們把外層RecyclerView的根佈局換成了NestedScrollingParent2LayoutImpl3,運行後發現確實解決了上述問題,滑動效果同京東一致。
那NestedScrollingParent2LayoutImpl3這是啥呢?NestedScrollingParent2LayoutImpl3是繼承NestedScrollingParent2的LinearLayout,用於處理上述嵌套滑動帶來的問題。(點擊查看NestedScrollingParent2LayoutImpl3的實現

效果以下:
在這裏插入圖片描述

若是不關心原理及實現,到這了就結束了,由於NestedScrollingParent2LayoutImpl3就能夠解決以上問題。

2、NestedScrollingParent2LayoutImpl3的實現原理

2.1 先來回顧下嵌套滑動機制。

若是還不瞭解嵌套滑動以及NestedScrollingParent2,建議先閱讀此篇博客自定義View事件之進階篇(一)-NestedScrolling(嵌套滑動)機制,再接着往下閱讀。

NestedScrolling(嵌套滑動)機制,簡單說來就是:產生嵌套滑動的子view,在滑動前,先詢問 嵌套滑動對應的父view 是否優先處理 事件、以及消費多少事件,而後把消費後剩餘的部分 繼續給到 子view。 能夠理解爲一個事件序列分發兩次。產生嵌套滑動的子view要實現接口NestedScrollingChild二、父view要實現接口NestedScrollingParent2。

經常使用的RecyclerView就是實現了NestedScrollingChild2,而NestedScrollView則是既實現了NestedScrollingChild2又實現了NestedScrollingParent2。

一般咱們要自行手動處理的就是RecyclerView做爲嵌套滑動子view的狀況。NestedScrollView通常直接做爲根佈局用來解決嵌套滑動。

2.2 再來看看NestedScrollView嵌套RecyclerView

關於NestedScrollView嵌套RecyclerView的狀況,即頭部和列表能夠一塊兒滑動。以下圖:
NestedScrollView嵌套RecyclerView
參考這篇實名反對《阿里巴巴Android開發手冊》中NestedScrollView嵌套RecyclerView的用法。今後篇文章分析結論得知,NestedScrollView嵌套RecyclerView雖然能夠實現效果,可是RecyclerView會瞬間加載全部item,RecyclerView失去的view回收的特性。 做者最後建議使用RecyclerView多佈局。
但其實在真實應用中,可能 頭部 和 列表 的數據來自不一樣的接口,當列表的數據請求失敗時要展現缺省圖,但頭部仍是會展現。這時頭部和列表 分開實現 是比較好的選擇。


這裏給出解決方案:

<?xml version="1.0" encoding="utf-8"?>
<com.hfy.demo01.module.home.touchevent.view.NestedScrollingParent2LayoutImpl2 xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_head"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:padding="15dp"
        android:text="我是頭部。 最外層是NestedScrollingParent2LayoutImpl2"
        android:textColor="#fff"
        android:textSize="20dp" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/design_default_color_primary" />

</com.hfy.demo01.module.home.touchevent.view.NestedScrollingParent2LayoutImpl2>

NestedScrollingParent2LayoutImpl2一樣是實現了NestedScrollingParent2。(點擊查看NestedScrollingParent2LayoutImpl2的實現

效果以下,可見滑動流暢,臨界處不用擡起手指從新滑,且查看日誌不是一次加載完item。
在這裏插入圖片描述

先看下NestedScrollingParent2LayoutImpl2的實現,要簡單一些,接着再看NestedScrollingParent2LayoutImpl3實現原理,總體思路是一致的。

/** * 處理 header + recyclerView * Description:NestedScrolling2機制下的嵌套滑動,實現NestedScrollingParent2接口下,處理fling效果的區別 * */
public class NestedScrollingParent2LayoutImpl2 extends NestedScrollingParent2Layout implements NestedScrollingParent2 {


    private View mTopView;
    private View mRecylerVIew;

    private int mTopViewHeight;


    public NestedScrollingParent2LayoutImpl2(Context context) {
        this(context, null);
    }

    public NestedScrollingParent2LayoutImpl2(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NestedScrollingParent2LayoutImpl2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOrientation(VERTICAL);
    }


    @Override
    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) {
        return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
    }


    /** * 在嵌套滑動的子View未滑動以前,判斷父view是否優先與子view處理(也就是父view能夠先消耗,而後給子view消耗) * * @param target 具體嵌套滑動的那個子類 * @param dx 水平方向嵌套滑動的子View想要變化的距離 * @param dy 垂直方向嵌套滑動的子View想要變化的距離 dy<0向下滑動 dy>0 向上滑動 * @param consumed 這個參數要咱們在實現這個函數的時候指定,回頭告訴子View當前父View消耗的距離 * consumed[0] 水平消耗的距離,consumed[1] 垂直消耗的距離 好讓子view作出相應的調整 * @param type 滑動類型,ViewCompat.TYPE_NON_TOUCH fling效果,ViewCompat.TYPE_TOUCH 手勢滑動 */
    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        //這裏無論手勢滾動仍是fling都處理
        boolean hideTop = dy > 0 && getScrollY() < mTopViewHeight;
        boolean showTop = dy < 0 && getScrollY() >= 0 && !target.canScrollVertically(-1);
        if (hideTop || showTop) {
            scrollBy(0, dy);
            consumed[1] = dy;
        }
    }


    @Override
    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
        //當子控件處理完後,交給父控件進行處理。
        if (dyUnconsumed < 0) {
            //表示已經向下滑動到頭
            scrollBy(0, dyUnconsumed);
        }
    }

    @Override
    public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
        return false;
    }


    @Override
    public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {
        return false;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //這裏修改mRecylerVIew的高度爲屏幕高度,不然底部會出現空白。(由於scrollTo方法是滑動子view,就把mRecylerVIew滑上去了)
        ViewGroup.LayoutParams layoutParams = mRecylerVIew.getLayoutParams();
        layoutParams.height = getMeasuredHeight();
        mRecylerVIew.setLayoutParams(layoutParams);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTopView = findViewById(R.id.tv_head);
        mRecylerVIew = findViewById(R.id.recyclerView);
        if (!(mRecylerVIew instanceof RecyclerView)) {
            throw new RuntimeException("id RecyclerView should be RecyclerView!");
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTopViewHeight = mTopView.getMeasuredHeight();
    }

    @Override
    public void scrollTo(int x, int y) {
        if (y < 0) {
            y = 0;
        }
        if (y > mTopViewHeight) {
            y = mTopViewHeight;
        }
        super.scrollTo(x, y);
    }

}

主要就是再onNestedPreScroll中對臨界處作了處理:滑動RecyclerView時先滑動根佈局,使得頭部隱藏或顯示,而後再交給RecyclerView滑動。

2.3 NestedScrollingParent2LayoutImpl3的實現原理

代碼以下

/** * 處理RecyclerView 套viewPager, viewPager內的fragment中 也有RecyclerView,處理外層、內層 RecyclerView的嵌套滑動問題 * 相似淘寶、京東首頁 * */
public class NestedScrollingParent2LayoutImpl3 extends NestedScrollingParent2Layout {

    private final String TAG = this.getClass().getSimpleName();

    private RecyclerView mParentRecyclerView;


    private RecyclerView mChildRecyclerView;

    private View mLastItemView;


    public NestedScrollingParent2LayoutImpl3(Context context) {
        super(context);
    }

    public NestedScrollingParent2LayoutImpl3(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public NestedScrollingParent2LayoutImpl3(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOrientation(VERTICAL);
    }


    /** * 有嵌套滑動到來了,判斷父view是否接受嵌套滑動 * * @param child 嵌套滑動對應的父類的子類(由於嵌套滑動對於的父View不必定是一級就能找到的,可能挑了兩級父View的父View,child的輩分>=target) * @param target 具體嵌套滑動的那個子類 * @param nestedScrollAxes 支持嵌套滾動軸。水平方向,垂直方向,或者不指定 * @param type 滑動類型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手勢滑動 */
    @Override
    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int nestedScrollAxes, int type) {
        //本身處理邏輯
        //這裏處理是接受 豎向的 嵌套滑動
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    /** * 在嵌套滑動的子View未滑動以前,判斷父view是否優先與子view處理(也就是父view能夠先消耗,而後給子view消耗) * * @param target 具體嵌套滑動的那個子類,就是手指滑的那個 產生嵌套滑動的view * @param dx 水平方向嵌套滑動的子View想要變化的距離 * @param dy 垂直方向嵌套滑動的子View想要變化的距離 dy<0向下滑動 dy>0 向上滑動 * @param consumed 這個參數要咱們在實現這個函數的時候指定,回頭告訴子View當前父View消耗的距離 * consumed[0] 水平消耗的距離,consumed[1] 垂直消耗的距離 好讓子view作出相應的調整 * @param type 滑動類型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手勢滑動 */
    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        //本身處理邏輯

        if (mLastItemView == null) {
            return;
        }

        int lastItemTop = mLastItemView.getTop();

        if (target == mParentRecyclerView) {
            handleParentRecyclerViewScroll(lastItemTop, dy, consumed);
        } else if (target == mChildRecyclerView) {
            handleChildRecyclerViewScroll(lastItemTop, dy, consumed);
        }
    }

    /** * 滑動外層RecyclerView時,的處理 * * @param lastItemTop tab到屏幕頂部的距離,是0就表明到頂了 * @param dy 目標滑動距離, dy>0 表明向上滑 * @param consumed */
    private void handleParentRecyclerViewScroll(int lastItemTop, int dy, int[] consumed) {
        //tab上邊沒到頂
        if (lastItemTop != 0) {
            if (dy > 0) {
                //向上滑
                if (lastItemTop > dy) {
                    //tab的top>想要滑動的dy,就讓外部RecyclerView自行處理
                } else {
                    //tab的top<=想要滑動的dy,先滑外部RecyclerView,滑距離爲lastItemTop,恰好到頂;剩下的就滑內層了。
                    consumed[1] = dy;
                    mParentRecyclerView.scrollBy(0, lastItemTop);
                    mChildRecyclerView.scrollBy(0, dy - lastItemTop);
                }
            } else {
                //向下滑,就讓外部RecyclerView自行處理
            }
        } else {
            //tab上邊到頂了
            if (dy > 0){
                //向上,內層直接消費掉
                mChildRecyclerView.scrollBy(0, dy);
                consumed[1] = dy;
            }else {
                int childScrolledY = mChildRecyclerView.computeVerticalScrollOffset();
                if (childScrolledY > Math.abs(dy)) {
                    //內層已滾動的距離,大於想要滾動的距離,內層直接消費掉
                    mChildRecyclerView.scrollBy(0, dy);
                    consumed[1] = dy;
                }else {
                    //內層已滾動的距離,小於想要滾動的距離,那麼內層消費一部分,到頂後,剩的還給外層自行滑動
                    mChildRecyclerView.scrollBy(0, -(Math.abs(dy)-childScrolledY));
                    consumed[1] = -(Math.abs(dy)-childScrolledY);
                }
            }
        }

    }

    /** * 滑動內層RecyclerView時,的處理 * * @param lastItemTop tab到屏幕頂部的距離,是0就表明到頂了 * @param dy * @param consumed */
    private void handleChildRecyclerViewScroll(int lastItemTop, int dy, int[] consumed) {
        //tab上邊沒到頂
        if (lastItemTop != 0) {
            if (dy > 0) {
                //向上滑
                if (lastItemTop > dy) {
                    //tab的top>想要滑動的dy,外層直接消耗掉
                    mParentRecyclerView.scrollBy(0, dy);
                    consumed[1] = dy;
                } else {
                    //tab的top<=想要滑動的dy,先滑外層,消耗距離爲lastItemTop,恰好到頂;剩下的就滑內層了。
                    mParentRecyclerView.scrollBy(0, lastItemTop);
                    consumed[1] = dy - lastItemTop;
                }
            } else {
                //向下滑,外層直接消耗
                mParentRecyclerView.scrollBy(0, dy);
                consumed[1] = dy;
            }
        }else {
            //tab上邊到頂了
            if (dy > 0){
                //向上,內層自行處理
            }else {
                int childScrolledY = mChildRecyclerView.computeVerticalScrollOffset();
                if (childScrolledY > Math.abs(dy)) {
                    //內層已滾動的距離,大於想要滾動的距離,內層自行處理
                }else {
                    //內層已滾動的距離,小於想要滾動的距離,那麼內層消費一部分,到頂後,剩的外層滑動
                    mChildRecyclerView.scrollBy(0, -childScrolledY);
                    mParentRecyclerView.scrollBy(0, -(Math.abs(dy)-childScrolledY));
                    consumed[1] = dy;
                }
            }
        }
    }


    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        //直接獲取外層RecyclerView
        mParentRecyclerView = getRecyclerView(this);
        Log.i(TAG, "onFinishInflate: mParentRecyclerView=" + mParentRecyclerView);

        //關於內層RecyclerView:此時還獲取不到ViewPager內fragment的RecyclerView,須要在加載ViewPager後 fragment可見時 傳入
    }

    private RecyclerView getRecyclerView(ViewGroup viewGroup) {
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childAt = getChildAt(i);
            if (childAt instanceof RecyclerView) {
                if (mParentRecyclerView == null) {
                    return (RecyclerView) childAt;
                }
            }
        }
        return null;
    }

    /** * 傳入內部RecyclerView * * @param childRecyclerView */
    public void setChildRecyclerView(RecyclerView childRecyclerView) {
        mChildRecyclerView = childRecyclerView;
    }


    /** * 外層RecyclerView的最後一個item,即:tab + viewPager * 用於判斷 滑動 臨界位置 * * @param lastItemView */
    public void setLastItem(View lastItemView) {
        mLastItemView = lastItemView;
    }
}

NestedScrollingParent2LayoutImpl3 繼承自 NestedScrollingParent2Layout。NestedScrollingParent2Layout是繼承自 LinearLayout implements 並實現了NestedScrollingParent2,主要處理了通用的方法實現。

/** * Description: 通用 滑動嵌套處理佈局,用於處理含有{@link androidx.recyclerview.widget.RecyclerView}的嵌套套滑動 */
public class NestedScrollingParent2Layout extends LinearLayout implements NestedScrollingParent2 {


    private NestedScrollingParentHelper mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);

    public NestedScrollingParent2Layout(Context context) {
        super(context);
    }

    public NestedScrollingParent2Layout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public NestedScrollingParent2Layout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /** * 有嵌套滑動到來了,判斷父view是否接受嵌套滑動 * * @param child 嵌套滑動對應的父類的子類(由於嵌套滑動對於的父View不必定是一級就能找到的,可能挑了兩級父View的父View,child的輩分>=target) * @param target 具體嵌套滑動的那個子類 * @param nestedScrollAxes 支持嵌套滾動軸。水平方向,垂直方向,或者不指定 * @param type 滑動類型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手勢滑動 */
    @Override
    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int nestedScrollAxes, int type) {
        //本身處理邏輯
        return true;
    }

    /** * 當父view接受嵌套滑動,當onStartNestedScroll方法返回true該方法會調用 * * @param type 滑動類型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手勢滑動 */
    @Override
    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {
        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type);
    }

    /** * 在嵌套滑動的子View未滑動以前,判斷父view是否優先與子view處理(也就是父view能夠先消耗,而後給子view消耗) * * @param target 具體嵌套滑動的那個子類 * @param dx 水平方向嵌套滑動的子View想要變化的距離 * @param dy 垂直方向嵌套滑動的子View想要變化的距離 dy<0向下滑動 dy>0 向上滑動 * @param consumed 這個參數要咱們在實現這個函數的時候指定,回頭告訴子View當前父View消耗的距離 * consumed[0] 水平消耗的距離,consumed[1] 垂直消耗的距離 好讓子view作出相應的調整 * @param type 滑動類型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手勢滑動 */
    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        //本身處理邏輯
    }

    /** * 嵌套滑動的子View在滑動以後,判斷父view是否繼續處理(也就是父消耗必定距離後,子再消耗,最後判斷父消耗不) * * @param target 具體嵌套滑動的那個子類 * @param dxConsumed 水平方向嵌套滑動的子View滑動的距離(消耗的距離) * @param dyConsumed 垂直方向嵌套滑動的子View滑動的距離(消耗的距離) * @param dxUnconsumed 水平方向嵌套滑動的子View未滑動的距離(未消耗的距離) * @param dyUnconsumed 垂直方向嵌套滑動的子View未滑動的距離(未消耗的距離) * @param type 滑動類型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手勢滑動 */
    @Override
    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
        //本身處理邏輯
    }

    /** * 嵌套滑動結束 * * @param type 滑動類型,ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手勢滑動 */
    @Override
    public void onStopNestedScroll(@NonNull View child, int type) {
        mNestedScrollingParentHelper.onStopNestedScroll(child, type);
    }

    @Override
    public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
        //本身判斷是否處理
        return false;
    }

    @Override
    public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {
        //本身處理邏輯
        return false;
    }


    @Override
    public int getNestedScrollAxes() {
        return mNestedScrollingParentHelper.getNestedScrollAxes();
    }

}

實現原理主要在onNestedPreScroll方法,即嵌套滑動的子view滑動前,詢問對應的父view是否優先處理,以及處理多少。

因此不管滑動外城RecyclerView仍是內層RecyclerView,都會詢問NestedScrollingParent2LayoutImpl3,即都會走到onNestedPreScroll方法。而後根據tabLayout的位置以及滑動的方向,決定是滑動外層RecyclerView仍是滑內層,以及滑動多少。至關於一個事假序列分發了兩次,避免了常規事件分發 父view攔截後子view沒法處理的問題。

onNestedPreScroll中的具體處理,請看代碼,有詳細註釋。要結合滑動實際狀況去理解,便於遇到其餘狀況也能一樣處理。

這裏列出已經實現的處理三種嵌套滑動的方案

  • NestedScrollingParent2LayoutImpl1:處理 header + tab + viewPager + recyclerView
  • NestedScrollingParent2LayoutImpl2: 處理 header + recyclerView
  • NestedScrollingParent2LayoutImpl3:處理RecyclerView 套viewPager, viewPager內的fragment中 也有RecyclerView,處理外層、內層 RecyclerView的嵌套滑動問題,相似淘寶、京東首頁。

在這裏插入圖片描述

Demo Github地址,有幫助的話Star一波吧。

歡迎關注
在這裏插入圖片描述

發佈了56 篇原創文章 · 獲贊 19 · 訪問量 3萬+
相關文章
相關標籤/搜索