Android嵌套滾動NestedScroll的實現(xiàn)了解一下
其實嵌套滾動已經(jīng)算一個比較常見的特效了,下面這個動圖就是嵌套滾動的一個例子:

看到這個動效,大家可能都知道可以用CoordinatorLayout去實現(xiàn).其實CoordinatorLayout是基于NestedScroll機制去實現(xiàn)的,而我們直接通過NestedScroll機制也能很方便的實現(xiàn)這個動效.
原理
NestedScroll的其實很簡單.
一般的觸摸消息的分發(fā)都是從外向內(nèi)的,由外層的ViewGroup的dispatchTouchEvent方法調(diào)用到內(nèi)層的View的dispatchTouchEvent方法.
而NestedScroll提供了一個反向的機制,內(nèi)層的view在接收到ACTION_MOVE的時候,將滾動消息先傳回給外層的ViewGroup,看外層的ViewGroup是不是需要消耗一部分的移動,然后內(nèi)層的View再去消耗剩下的移動.內(nèi)層view可以消耗剩下的滾動的一部分,如果還沒有消耗完,外層的view可以再選擇把最后剩下的滾動消耗掉.
上面的描述可能有點繞,可以看下面的圖來幫助理解:
具體實現(xiàn)
NestedScroll機制會涉及到四個類:
NestedScrollingChild, NestedScrollingChildHelper 和 NestedScrollingParent , NestedScrollingParentHelper
NestedScrollingChild和NestedScrollingParent是兩個接口,我們先看看他們的聲明:
public interface NestedScrollingChild {
public void setNestedScrollingEnabled(boolean enabled);
public boolean isNestedScrollingEnabled();
public boolean startNestedScroll(int axes);
public void stopNestedScroll();
public boolean hasNestedScrollingParent();
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
public boolean dispatchNestedPreFling(float velocityX, float velocityY);
}
public interface NestedScrollingParent {
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
public void onStopNestedScroll(View target);
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed);
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
public boolean onNestedPreFling(View target, float velocityX, float velocityY);
public int getNestedScrollAxes();
}
這里真正重要的其實是NestedScrollingParent的幾個方法,因為其他方法都能直接讓NestedScrollingChildHelper或者NestedScrollingParentHelper去代理:
- onStartNestedScroll 是否接受嵌套滾動,只有它返回true,后面的其他方法才會被調(diào)用
- onNestedPreScroll 在內(nèi)層view處理滾動事件前先被調(diào)用,可以讓外層view先消耗部分滾動
- onNestedScroll 在內(nèi)層view將剩下的滾動消耗完之后調(diào)用,可以在這里處理最后剩下的滾動
- onNestedPreFling 在內(nèi)層view的Fling事件處理之前被調(diào)用
- onNestedFling 在內(nèi)層view的Fling事件處理完之后調(diào)用
我們只要讓子view和父view分別實現(xiàn)NestedScrollingChild和NestedScrollingParent接口,然后分別調(diào)用NestedScrollingChildHelper和NestedScrollingParentHelper的對應方法去代理一些具體功能,然后在NestedScrollingChild的onTouchEvent那里根據(jù)需求調(diào)用startNestedScroll/dispatchNestedPreScroll/stopNestedScroll就能實現(xiàn)嵌套滾動了:
//NestedScrollingChild
private NestedScrollingChildHelper mHelper = new NestedScrollingChildHelper(this);
public boolean startNestedScroll(int axes) {
return mHelper.startNestedScroll(axes);
}
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
return mHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, offsetInWindow);
}
...
//NestedScrollingParent
private NestedScrollingParentHelper mHelper = new NestedScrollingParentHelper(this);
public void onNestedScrollAccepted(View child, View target, int axes) {
mHelper.onNestedScrollAccepted(child, target, axes);
}
public int getNestedScrollAxes() {
return mHelper.getNestedScrollAxes();
}
...
但是如果你使用sdk21及以上的版本,NestedScroll機制已經(jīng)直接集成到了View中了,你只需要直接重寫View的對應方法就好
布局
我們先看布局文件
<me.linjw.nestedscrolldemo.NestedScrollParentView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="@mipmap/ic_launcher" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:text="Title"
android:textAlignment="center"
android:textSize="20dp" />
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</me.linjw.nestedscrolldemo.NestedScrollParentView>
最外層是我們自定義的NestedScrollParentView,其實它是一個LinearLayout,內(nèi)部豎直排列了三個子view:
- 一個由FrameLayout包裹的ImageView
- 一個TextView
- 一個RecyclerView
代碼
為了簡便起見,我們先直接用sdk22的版本用重寫View方法的方式去實現(xiàn)它.
NestedScrollParentView中有兩個方法比較重要,嵌套滾動基本上就是由這兩個方法實現(xiàn)的:
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
return true;
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(target, dx, dy, consumed);
boolean headerScrollUp = dy > 0 && getScrollY() < mHeaderHeight;
boolean headerScrollDown = dy < 0 && getScrollY() > 0 && !target.canScrollVertically(-1);
if (headerScrollUp || headerScrollDown) {
scrollBy(0, dy);
consumed[1] = dy;
}
}
onStartNestedScroll 這個方法如果返回true的話代表接受由內(nèi)層傳來的滾動消息,我們直接返回true就好,否則后面的消息都接受不到
onNestedPreScroll 這個方法用于消耗內(nèi)層view的一部分滾動.我們需要將消耗掉的滾動存到counsumed中讓consumed知道.例如我們這里在頂部的FrameLayout需要移動的情況下會消耗掉所有的dy,這樣內(nèi)層的view(即RecyclerView)就不會滾動了.
這里的mHeaderHeight保存的是頂部的FrameLayout的高度:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mHeaderHeight = mHeader.getMeasuredHeight();
}
到這里基本上就實現(xiàn)了動圖的效果,是不是很簡單?
完整代碼可以參考 https://github.com/bluesky466/NestedScrollDemo/tree/sdk22
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android實現(xiàn)始終顯示overflow菜單的方法
這篇文章主要介紹了android實現(xiàn)始終顯示overflow菜單的方法,需要的朋友可以參考下2014-07-07
Android?Studio實現(xiàn)簡單頁面跳轉(zhuǎn)的詳細教程
這篇文章主要給大家介紹了關(guān)于Android?Studio實現(xiàn)簡單頁面跳轉(zhuǎn)的詳細教程,文中通過圖文介紹的非常詳細,對大家學習或者使用Android?Studio具有一定的參考學習價值,需要的朋友可以參考下2023-01-01
Android中ViewPager獲取當前顯示的Fragment
這篇文章主要介紹了Android中ViewPager獲取當前顯示的Fragment的兩種方法,一種是使用 getSupportFragmentManager().findFragmentByTag()方法,另一種是重寫適配器的 setPrimaryItem()方法,有需要的朋友可以參考借鑒,下面來一起看看吧。2017-01-01
Android使用RollViewPager實現(xiàn)輪播圖
這篇文章主要為大家詳細介紹了Android使用RollViewPager實現(xiàn)輪播圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-04-04
Android廣播接收機制詳細介紹(附短信接收實現(xiàn))
這篇文章主要介紹了Android廣播接收機制詳細介紹(附短信接收實現(xiàn)),本文講解了BroadcastReceiver的注冊過程BroadcastReceiver的步驟,并給出一個短信廣播實現(xiàn)示例,需要的朋友可以參考下2015-06-06
Android 開發(fā)使用Activity實現(xiàn)加載等待界面功能示例
這篇文章主要介紹了Android 開發(fā)使用Activity實現(xiàn)加載等待界面功能,結(jié)合實例形式詳細分析了Android基于Activity實現(xiàn)加載等待界面布局與功能操作技巧,需要的朋友可以參考下2020-05-05
Android事件分發(fā)機制(下) View的事件處理
這篇文章主要介紹了Android事件分發(fā)機制下篇, View的事件處理的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01

