溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android 中如何實現(xiàn)三級NestedScroll嵌套滾動

發(fā)布時間:2021-06-09 17:35:19 來源:億速云 閱讀:275 作者:Leah 欄目:移動開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)Android 中如何實現(xiàn)三級NestedScroll嵌套滾動,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

嵌套滾動介紹

我們知道 NestedScrolling(Parent/Child) 這對接口是用來實現(xiàn)嵌套滾動的,一般實現(xiàn)這對接口的 Parent 和 Child 沒有直接嵌套,否則直接用 onInterceptTouchEvent() 和 onTouchEvent() 這對方法實現(xiàn)就可以了。能夠越級嵌套滾動正是它的厲害之處。

嵌套滾動的接口有兩對:NestedScrolling(Parent/Child) 和 NestedScrolling(Parent2/Child2) 后者相比前者對 fling 的處理更加細(xì)致。相比第一代 Child 簡單地將 fling 拋給 Parent,第二代 Child 將 fling 轉(zhuǎn)化為 scroll 后再分發(fā)給 Parent,為了和普通的 scroll 區(qū)分增加了一個參數(shù) type, 當(dāng) type 是 ViewCompat.TYPE_TOUCH 時表示普通的 scroll,當(dāng)是 ViewCompat.TYPE_NON_TOUCH 時表示由 fling 轉(zhuǎn)化而來的 scroll。這樣做的好處是當(dāng) Child 檢測到一個 fling 時,它可以選擇將這個 fling 引起的 scroll 一部分作用在 Parent 上一部分作用在自己身上,而不是只作用在 Parent 或者 Child 上?;蛟S你會問 fling 為什么不能選擇 Parent 和 Child 都作用,事實上你可以,但 fling 的話 Parent 沒法告訴 Child 消費了多少,剩下多少,因為 fling 傳遞的值是速度,不像 scroll 是距離。所以通過 NestedScrolling(Parent2/Child2) 實現(xiàn)嵌套滾動時,當(dāng)你觸發(fā)了一個 fling 時,也可以做很順滑連貫的交替滾動,而 1 就很難達(dá)到相同的效果?,F(xiàn)在官方 View 的實現(xiàn)也是通過 NestedScrolling(Parent2/Child2),所以我們在實現(xiàn)自定義的嵌套滾動時盡量用 2。

上面簡單介紹了 NestedScrolling 2 和 1 的區(qū)別以及為什么要使用2。現(xiàn)在我們來看看 NestedScrolling(Parent2/Child2) 的方法,1 就不看了,和 2 差不多。

public interface NestedScrollingChild2 {

 void setNestedScrollingEnabled(boolean enabled);

 boolean isNestedScrollingEnabled();

 boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type);
 
 void stopNestedScroll(@NestedScrollType int type);

 boolean hasNestedScrollingParent(@NestedScrollType int type);

 boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
   int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow,
   @NestedScrollType int type);
   
	boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
   @Nullable int[] offsetInWindow, @NestedScrollType int type);
}
public interface NestedScrollingParent2 {

	boolean onStartNestedScroll(@NonNull View child, @NonNull View target, @ScrollAxis int axes,
   @NestedScrollType int type);
   
	void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes,
   @NestedScrollType int type);
   
	void onStopNestedScroll(@NonNull View target, @NestedScrollType int type);

	void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
   int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type);

	void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,
   @NestedScrollType int type);
}

從這兩個接口的方法可以看出這些方法都是一一對應(yīng)的,比如 startNestedScroll 和 onStartNestedScroll,stopNestedScroll 和 onStopNestedScroll 等。從這些方法的命名上也能看出來嵌套滾動的交互順序是 Child 主動觸發(fā),Parent 被動接受,所以決定是否打開嵌套滾動的方法 setNestedScrollingEnabled 由 Child 實現(xiàn),決定開始和結(jié)束的方法 startNestedScroll 和 stopNestedScroll 也由 Child 實現(xiàn)。

這里用一個圖來表示嵌套滾動流程

Android 中如何實現(xiàn)三級NestedScroll嵌套滾動

整個過程大概分為兩部分:綁定和滾動分發(fā)。綁定部分可以理解為 Child 向上遍歷找 NestedScrollingParent2 的過程,找到后調(diào)用它的 onStartNestedScroll 方法,如果返回 true 則說明這個 Parent 想接收 nested scroll,Child 會緊接著調(diào) onNestedScrollAccepted 方法表示同意 Parent 處理自己分發(fā)的 nested scroll,對應(yīng)上圖中的 1 2 3。滾動分發(fā)部分 Child 將自己的 scroll 分為三個階段 before scroll after,before 和 after 分發(fā)給 parent 消費,scroll 階段讓自己消費,這三個階段是按順序進(jìn)行的,換句話說如果前一步消耗完了 scroll,那后面的階段就沒有 scroll 可以消費。這樣做的好處是讓 Parent 可以在自己消費之前或者之后消費 scroll,如果 Parent 想在 Child 之前消費就在 onNestedPreScroll 方法里處理,否則就在 onNestedScroll 方法里,對應(yīng)上圖中的 4 5 步。上面介紹到的一些通用邏輯被封裝在 NestedScrollingChildHelper 和 NestedScrollingParentHelper 中,在 NestedScrolling(Parent2/Child2) 的方法中可以調(diào)用 Helper 類中的同名方法,比如 NestedScrollingChild2.startNestedScroll 方法中實現(xiàn)了向上遍歷尋找 NestedScrollingParent 的邏輯。

三級嵌套滾動

一個常見的嵌套滾動例子是 CoordinatorLayout/AppbarLayout - RecyclerView, 實現(xiàn)的效果是向上滑動列表時,會先將 AppbarLayout 向上滑動直到完全折疊,向下滑動至列表最頂部后會展開 AppbarLayout, 如下圖:

Android 中如何實現(xiàn)三級NestedScroll嵌套滾動

這里實現(xiàn) NestedScrollingParent2 的是 CoordinatorLayout/AppbarLayout, 實現(xiàn) NestedScrollingChild2 的是 RecyclerView。對于這種兩級嵌套滾動的需求使用 CoordinatorLayout 幾乎都能實現(xiàn),如果遇到特殊的業(yè)務(wù)需求基于 CoordinatorLayout 和 RecyclerView 的實現(xiàn)改改也能實現(xiàn)。

Android 中如何實現(xiàn)三級NestedScroll嵌套滾動

我這里遇到的需求是即刻首頁的樣式(可參考即刻5.4.2版本),除了要有 AppbarLayout 折疊效果之外還要在 AppbarLayout 頂部展示搜索框和刷新動畫。這里的滑動邏輯是:

  • 向上滑動時,最先折疊刷新動畫,向下滑動時最后展開刷新動畫。

  • 向上滑動列表時先折疊 AppbarLayout,AppbarLayout 完全折疊后再折疊搜索框。

  • 向下滑動列表時在展開 AppbarLayout 之前先展開搜索框。

  • 列表沒滑動到頂部時可以通過觸發(fā)一定速度的向下 fling 來展開搜索框。

可以發(fā)現(xiàn)這里除了 CoordinatorLayout/AppbarLayout - RecyclerView 這對嵌套滾動的 Parent 和 Child 之外還多了搜索框和刷新動畫,而這三者之間的滑動邏輯需要通過嵌套滾動實現(xiàn),只是傳統(tǒng)的兩級嵌套滾動不能滿足,所以需要實現(xiàn)三級嵌套滾動。

所謂三級嵌套滾動是在兩級嵌套滾動之上再添加一個 Parent,這里為了表述方便將三級嵌套滾動的三級由上到下分別稱為 Grand Parent Child。具體是由兩對 NestedScrolling(Parent2/Child2) 接口實現(xiàn),Grand 實現(xiàn)第一對接口的 Parent,Parent 實現(xiàn)第一對接口的 Child 和第二對接口的 Parent,Child 實現(xiàn)第二對接口的 Child。與兩級嵌套滾動相比三級嵌套的 Grand 和 Child 和兩級的 Parent 和 Child 區(qū)別不大,變化比較大的是三級的 Parent 既要實現(xiàn)兩級的 Parent 接口又要實現(xiàn) Child 接口,示意圖如下:

Android 中如何實現(xiàn)三級NestedScroll嵌套滾動

在即刻首頁這個例子里,CoordinatorLayout/AppbarLayout 屬于三級嵌套的 Parent 實現(xiàn)了第二對接口的 NestedScrollingParent2,RecyclerView 屬于 Child 實現(xiàn)了第二對接口的 NestedScrollingChild2。這里我們需要做的是實現(xiàn)第一對嵌套接口,新建一個自定義 Layout 實現(xiàn) NestedScrollingParent2 接口作為三級嵌套的 Grand,負(fù)責(zé)搜索框和刷新動畫的折疊和展開。再新建一個自定義 Layout 繼承 CoordinatorLayout 實現(xiàn) NestedScrollingChild2 接口,負(fù)責(zé)攔截列表分發(fā)上來的滾動事件或者處理 AppbarLayout 消費后剩下的滾動事件。

二級嵌套滾動可以理解為給 Parent 提供了攔截 Child 滾動事件和處理 Child 剩余滾動事件的能力,具體邏輯可參考本文最開始介紹嵌套滾動的部分。相應(yīng)的三級嵌套滾動給 Grand 提供了攔截 Parent 和處理剩余滾動事件的能力,只是攔截和處理的時機多了一些,如下圖:

Android 中如何實現(xiàn)三級NestedScroll嵌套滾動

二級嵌套滾動對滾動處理時機只有三個階段:preScroll、scroll 和 afterScroll。而三級嵌套滾動的處理時機就多一些,有七個階段:prePreScroll、preScroll、afterPreScroll、scroll、preAfterScroll、afterScroll 和 afterAfterScroll,可以看出相比二級嵌套多了 prePreScroll、afterPreScroll、preAfterScroll 和 afterAfterScroll 這四個階段,多出的這幾個階段都是給 Grand 用的。到這里可以發(fā)現(xiàn) NestedScrollingParent2 其實不能完全描述 Grand 的能力,確實最理想的方案應(yīng)該是新建一對接口 NestedScrollingGrand2 和 NestedScrollingGrandChild2 來描述新增的四個對滾動事件的處理階段,但考慮到我這里的例子 Grand 對 Parent 的處理沒有那么精細(xì)化,所以還是通過復(fù)用 NestedScrolling(Parent2/Child2) 和一些附加方法來實現(xiàn)。以后如果實現(xiàn)了 NestedScrolling(Grand2/GrandChild2) 接口,也會及時更新。根據(jù)上圖即刻首頁滑動的實現(xiàn)思路就很簡單了:

  • onPrePreScroll 中執(zhí)行折疊刷新動畫的邏輯,onAfterAfterScroll 中執(zhí)行展開刷新動畫的邏輯。

  • onPreScroll 中執(zhí)行折疊 AppbarLayout 的邏輯,onAfterPreScroll 中執(zhí)行搜索框折疊的邏輯。

  • onAfterScroll 中執(zhí)行展開 AppbarLayout 的邏輯,onPreAfterScroll 中執(zhí)行搜索框展開的邏輯。

  • 列表沒滑到頂部根據(jù) fling 展開搜索框的邏輯單獨在 Parent 的 onNestedPreFling 里做,這條算是一個特殊處理。

關(guān)于Android 中如何實現(xiàn)三級NestedScroll嵌套滾動就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI