NestedScrolling機(jī)制的學(xué)習(xí)筆記(一)

我是昨天才知道Android里有NestedScrolling這個(gè)東西的。
所以我昨天學(xué)習(xí)了一下,然后今天來總結(jié)一下我昨天學(xué)習(xí)的內(nèi)容。

首先NestedScrolling機(jī)制可以很方便的實(shí)現(xiàn)如下的效果:

1.gif

上述例子參考自:http://blog.csdn.net/al4fun/article/details/53889075
寫的很好,大家如果接下來看不懂我寫的,可以去上面那個(gè)博客看。

好了,我們開始學(xué)習(xí)一下這個(gè)機(jī)制吧。

NestedScrolling:

在正式的介紹之前,我們先說說NestedScrolling是什么。
它中文叫做嵌套滑動(dòng)機(jī)制,說起嵌套滑動(dòng)我們會(huì)想起啥?
滑動(dòng)沖突呀!那說起滑動(dòng)沖突我們會(huì)想起啥?
事件分發(fā)呀!然后就暴露出了事件分發(fā)機(jī)制的一個(gè)問題:
<blockquote>
事件分發(fā)中,如果子元素有機(jī)會(huì)處理一個(gè)事件序列,父容器就沒有機(jī)會(huì)處理這一事件序列了,直到下一個(gè)序列產(chǎn)生。
</blockquote>
那也就是說,我們滑動(dòng)子元素,子元素如果不想處理這個(gè)Touch事件,只能丟掉它,而不能交給父容器去處理。

于是NestedScrolling機(jī)制出現(xiàn)了,它會(huì)在子元素每次處理一個(gè)Touch事件之前,詢問一下父元素,這就解決了事件分發(fā)機(jī)制的這個(gè)缺陷。

那我們接下來就來講講如何使用NestedScrolling機(jī)制吧。

接口介紹:

對(duì)于NestedScrolling機(jī)制,Android提供了四個(gè)類,分別為:
1:NestedScrollingParent
2:NestedScrollingChild
3:NestedScrollingParentHelper
4:NestedScrollingChildHelper

如果要實(shí)現(xiàn)NestedScrolling機(jī)制,那么我們的子元素得實(shí)現(xiàn)NestedScrollingChild接口,而與之配合的父容器得實(shí)現(xiàn)NestedScrollingParent接口。

剩下的兩個(gè),顧名思義,就是Android為我們提供的幫助類,幫助我們實(shí)現(xiàn)接口里函數(shù)的,等到了具體的實(shí)例時(shí)你就會(huì)發(fā)現(xiàn),真正要我們自己實(shí)現(xiàn)的接口里的函數(shù)很少,大多數(shù)只要調(diào)用幫助類的同名函數(shù)就可以了。

接口函數(shù)介紹:

NestedScrollingChild:
    @Override
    public void setNestedScrollingEnabled(boolean enabled) {

    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return false;
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return false;
    }

    @Override
    public void stopNestedScroll() {

    }

    @Override
    public boolean hasNestedScrollingParent() {
        return false;
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return false;
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return false;
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return false;
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return false;
    }

別被這么多函數(shù)嚇到了,就像我剛才說了,真的需要我們具體實(shí)現(xiàn)的函數(shù)很少,大多都交給了幫助類,那我們就來看看其中主要的四個(gè)方法來了解一下大致的流程:

public boolean startNestedScroll(int axes)

開啟NestedScrolling機(jī)制,通知父容器,我要和你配合處理Touch事件啦。這個(gè)函數(shù)內(nèi)部會(huì)去尋找實(shí)現(xiàn)了NestedScrolling機(jī)制的父容器,如果找到了就返回true,如果沒有則返回false。

public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)

這個(gè)函數(shù)會(huì)在子元素滑動(dòng)之前詢問父容器要不要消費(fèi)這些距離。dx和dy是滑動(dòng)的距離,這兩個(gè)參數(shù)會(huì)傳給父容器,父容器可以選擇是否消費(fèi)這些距離(可以消費(fèi)部分,也可以全部消費(fèi)),而consumed和offsetInWindow則是父容器回傳過來的數(shù)據(jù),consumed里面存放著父容器消費(fèi)掉的距離,consumed[0]是x軸上的距離,consumed[1]是y軸上的距離。offsetInWindow里面存放著父容器位置的偏移量,
offsetInWindow[0]是x軸的,offsetInWindow[1]是y軸的。如果父容器接受了參數(shù),進(jìn)行了消費(fèi),那么就返回true,否則就返回false。

public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) 

這個(gè)函數(shù)在子元素滑動(dòng)之后調(diào)用,會(huì)詢問父容器要不要消費(fèi)余下(Unconsumed)的滾動(dòng)。前四個(gè)參數(shù)分別為x軸已消耗的距離,y軸已消耗的距離,以及x,y軸未消耗的距離,offsetInWindow與上個(gè)函數(shù)相同。如果父容器接受了參數(shù),進(jìn)行了消費(fèi),那么就返回true,否則就返回false。

public void stopNestedScroll()

這個(gè)函數(shù)用來結(jié)束整個(gè)流程。

NestedScrollingParent:
    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return false;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {

    }

    @Override
    public void onStopNestedScroll(View target) {

    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {

    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {

    }

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

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

    @Override
    public int getNestedScrollAxes() {
        return 0;
    }

因?yàn)镹estedScrolling機(jī)制一般都是子元素發(fā)起調(diào)用,然后父容器接受回調(diào),所以我們這里看看子元素和父容器對(duì)應(yīng)的方法,參考一下子元素的參數(shù)即可,就不再重復(fù)講了:

圖片來源于https://segmentfault.com/a/1190000002873657

流程總結(jié):

以觸摸滑動(dòng)為例,慣性滑動(dòng)(fling)的流程與此類似:

首先調(diào)用子元素的startNestedScroll()來發(fā)起嵌套滾動(dòng)流程,函數(shù)內(nèi)部會(huì)尋找實(shí)現(xiàn)NestedScrolling機(jī)制的父容器,然后父容器的onStartNestedScroll()會(huì)被回調(diào),如果此方法返回true,則onNestedScrollAccepted()也會(huì)被回調(diào)。

子元素每次滑動(dòng)前,可以調(diào)用dispatchNestedPreScroll()詢問父容器是否要消費(fèi),這會(huì)回調(diào)父容器的onNestedPreScroll(),然后父容器就可以在這個(gè)回調(diào)中先于子元素滑動(dòng)。

disdispatchNestedPreScroll()之后,子元素可以進(jìn)行自己的滑動(dòng)操作。

子元素滑動(dòng)以后,可以調(diào)用dispatchNestedScroll(),會(huì)回調(diào)到父容器的onNestedScroll(),在這里父容器可以繼續(xù)消費(fèi)子元素沒有消費(fèi)完的距離。

滑動(dòng)結(jié)束,調(diào)用stopNestedScroll()。

結(jié)束:

這篇文章先到此結(jié)束,就是大概的介紹一下NestedScrolling是啥,下一篇會(huì)講最開始的那個(gè)例子是怎么實(shí)現(xiàn)的,讓大家更好的了解NestedScrolling機(jī)制。

如果有問題,請(qǐng)?jiān)谠u(píng)論區(qū)留言,才疏學(xué)淺,歡迎大家批評(píng)指正。

最后的最后:

感謝我可愛的女朋友。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容