您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關Android中怎么利用NestedScrolling實現嵌套滑動機制,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
具體效果可以對比一下:
說到Gemini,我也是這兩天因為了解NestedScrolling時接觸到的,粗略看了一下資料和文章瀏覽數,贊! 我的大神!
好,前番就到這了,開始正題NestedScrolling。
之前了解NestedScrolling的時候看過一些博客,其中就包括Gemini的segmentfault,當時看的時候因為不仔細不以為然,***才發現這篇博客是對NestedScrolling介紹最清楚的,作為懲罰也好膜拜也罷,把本來可以cv過來的博客手動敲一遍,順便補充一下自己的一些額外理解。
再次感謝Gemini
Android 在發布 Lillipop 版本后,為了更好的用戶體驗,Google為Android的滑動機制提供了NestedScrolling機制。
NestedScrolling的特性可以體現在哪兒呢?
比如你用了Toolbar,下面一個ScrollView,向上滾動隱藏Toolbar,向下滾動顯示Toolbar,這里在邏輯上就是一個NestedScrolling——因為你在滾動整個Toolbar在內的View的過程中,又嵌套滾動了里邊的ScrollView。
如圖:
在這之前,我們知道Android對Touch事件分發是有自己的一套機制。主要是有三個函數:
dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent。
這種分發機制有一個漏洞:
如果子view獲得處理touch事件機會的時候,父view就再也沒有機會處理此次touch事件,直到下一次手指觸發。
也就是說,我們在滑動子view的時候,如果子view對這個滑動事件不需要處理的時候,只能拋棄這個touch事件,而不會傳給父view去處理。
但Google新的NestedScrolling機制就很好的解決了這個問題。
NestedScrolling主要有四個類需要關注:
NestedScrollingChild
NestedScrollingChildHelper
NestedScrollingParent
NestedScrollingParentHelper
以上四個類都在support-v4包中提供,Lollipop中部分View默認實現了NestedScrollingChild或NestedScrollingParent。
v4包中NestedScrollView同時實現了NestedScrollingChild和NestedScrollingParent。
一般實現NestedScrollingChild就可以了,父View用support-design提供的實現了NestedScrollingParent的CoordinatorLayout即可。
@Override public void setNestedScrollingEnabled(boolean enabled) { super.setNestedScrollingEnabled(enabled); mChildHelper.setNestedScrollingEnabled(enabled); } @Override public boolean isNestedScrollingEnabled() { return mChildHelper.isNestedScrollingEnabled(); } @Override public boolean startNestedScroll(int axes) { return mChildHelper.startNestedScroll(axes); } @Override public void stopNestedScroll() { mChildHelper.stopNestedScroll(); } @Override public boolean hasNestedScrollingParent() { return mChildHelper.hasNestedScrollingParent(); } @Override public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow); } @Override public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); } @Override public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); } @Override public boolean dispatchNestedPreFling(float velocityX, float velocityY) { return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); }
簡單邏輯這樣就可以實現嵌套滑動。
以上接口都是業務邏輯中自己調用,NestedScrollingChildHelper是如何實現的呢? 先看一下startNestedScroll方法
/** * Start a new nested scroll for this view. * * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass * method/{@link NestedScrollingChild} interface method with the same signature to implement * the standard policy.</p> * * @param axes Supported nested scroll axes. * See {@link NestedScrollingChild#startNestedScroll(int)}. * @return true if a cooperating parent view was found and nested scrolling started successfully */ public boolean startNestedScroll(int axes) { if (hasNestedScrollingParent()) { // Already in progress return true; } if (isNestedScrollingEnabled()) { ViewParent p = mView.getParent(); View child = mView; while (p != null) { if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) { mNestedScrollingParent = p; ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes); return true; } if (p instanceof View) { child = (View) p; } p = p.getParent(); } } return false; }
可以看到這里是幫你實現了與NestedScrollingParent交互的一些方法。
ViewParentCompat是一個和父View交互的兼容類,判斷API version,如果在Lollipop上就調用View自帶的方法,否則判斷如果實現了NestedScrollingParent,則調用實現接口的方法。
子View與父View的交互流程如下:
一、startNestedScroll
首先子View需要開啟整個流程(通過屏幕滑動觸發touch事件),通過NestedScrollingChildHelper找到并通知實現了NestedScrollingParent的父View中onStartNestedScroll和onNestedScrollAccepted方法。
二、dispatchNestedPreScroll
在子View的onIterceptTouchEvent和onTouch中(一般在MontionEvent.ACTION_MOVE事件里),調用改方法通知父View的滑動距離,該方法的第三第四個參數返回父View消費掉的scroll長度和子View的窗口偏移量,如果這個scroll沒有被消費完,則子View處理剩余距離,由于窗口被移動,如果記錄了手指***的位置,需要根據第四個參數offsetInWindow計算偏移量,才能保證下一次touch事件的計算是正確的。
如果父View接受了滾動參數并部分消費,則該函數返回true,否則返回false。該函數一般在子View處理Scroll前調用。
三、dispatchNestedScroll
向父View匯報滾動情況,包括子View已消費和未消費的值。
如果父View接受了滾動參數,部分消費則函數返回true,否則返回false。
該函數一般在子View處理Scroll后調用。
四、stopNestedScroll
結束整個嵌套滑動流程。
流程中NestedScrollingChild和NestedScrollingParent對應如下:
NestedScrollingChildImpl | NestedScrollingParentImpl |
onStartNestedScroll | onStartNestedScroll , onNestedScrollAccepted |
dispatchNestedPreScroll | onNestedPreScroll |
dispatchNestedScroll | onNestedScroll |
stopNestedScroll | onStopNestedScroll |
一般是子View發起調用,父View接受回調。
需要關注dispatchNestedPreScroll中的consumed參數:
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) ;
該參數是一個int類型的數組,長度為2,***個元素是父View消費的x軸方向的滾動距離,第二個元素是父View消費的y軸方向的滾動距離,如果兩個值均不為0,則表示父View已消費滾動距離,則需要對子View滾動距離進行修正,正因為有該參數,使得處理滾動事件時思路更加清晰,不會像以前一樣被一堆滾動參數搞混。
自己理解的NestedScrolling簡要流程圖(不包含Fling事件及返回值的邏輯):
上述就是小編為大家分享的Android中怎么利用NestedScrolling實現嵌套滑動機制了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。