OverScroll彈性滾動和慣性滾動效果的實現原理——CoordinatorLayout+Behavior

前面介紹了OverScroll的使用,沒看過文章的同窗能夠先了解下《相似微信首頁彈性滾動和慣性滾動效果的實現——OverScroll》java

接下來介紹OverScroll的實現原理。android

CoordinatorLayout

CoordinatorLayout是在Support 包中功能強大的佈局容器,它本質是一個 FrameLayout,然而它容許開發者經過自定義Behavior協調各個子view,實現各類複雜酷炫的UI交互效果。git

使用CoordinatorLayout須要在 build.gradle 加入:github

implementation 'com.android.support:design:26.1.0'
複製代碼

網上不少關於CoordinatorLayout的入門文章,這裏筆者再也不贅述,所謂實踐大於理論,本文講述如何利用CoordinatorLayout+Behavior實現彈性滑動和慣性滑動,從側面去理解它的使用原理.微信

本文實現相似微信首頁的彈性滑動和慣性滑動效果,支持水平和垂直方向上的滾動,以下圖所示:app

vertical over-scroll
horizontal over-scroll

Behavior

CoordinatorLayout主要是經過Behavior來協調子view,這裏涉及到的Behavior的關鍵方法以下:工具

方法 描述
boolean onStartNestedScroll(CoordinatorLayout parent, View child, View directTargetChild, View target, int nestedScrollAxes, int type) 根據返回值判斷是否要處理target發生的滑動,通常用於判斷是否要處理某個方向上的滑動.
例如return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;表示處理垂直方向方向上的滑動.接下來的滑動事件將回調給下面的方法處理.
void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed, int type) target即將發生滑動時調用,在這裏能夠作攔截處理.能夠修改參數consumed表示消耗(攔截)了多少像素。例如target控件自己想要垂直方向上滑動100px,而咱們須要攔截掉80px,則要設置 consumed[1] = 80,(consumed[0]consumed[1] 分別對應x軸和y軸),最後target控件實際只滑動了20px.
void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) target控件發生滑動後調用, dyConsumed爲實際消耗的距離,dyUnconsumed爲未消耗的距離.例如上面的滑動,此時dyConsumed = 20,dxUnconsumed = 0,若是dyConsumed = 15,dxUnconsumed = 5則表示target在滑動15px距離時到達了邊界,咱們能夠利用dxUnconsumed處理一些越界後的滑動.
boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) 用戶快速滑動target並鬆開手指發生慣性滑動以前調用,返回true表示攔截該慣性滑動事件.
void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int type) 全部的滑動中止後調用.

原理分析

彈性滑動和慣性滑動都屬於過分滑動(Over scroll),即在達到正常滑動範圍的邊界後繼續滑動.所以,咱們只須要處理在達到邊界時的越界滑動效果.關鍵的處理邏輯以下:佈局

overscoll

上圖描述了向下滑動過程當中須要處理的關鍵邏輯,同理,向上滑動也採起相似的處理.post

另外咱們還要處理慣性滑動,當快速滑動產生fling事件時,讓列表滑到邊界時仍可以慣性滑動一點距離.這裏須要藉助系統提供的工具類OverScroller, 主要在onNestedPreFling裏相關的慣性滑動的參數傳入OverScrollergradle

public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
    if (child == target) {
        mOverScroller.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
    }
    return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
複製代碼

後面即可以在滑動過程當中經過mOverScroller.getCurrVelocity()獲取當前時間慣性滑動的速度,當速度小於某個值時則中止滑動.

Behavior中的關鍵方法的參數中基本上最後都有個type值,文檔解釋爲the type of input which cause this scroll event,即表示產生當前滑動的事件來源,當type == ViewCompat.TYPE_TOUCH時表示由用戶觸摸控件產生的滑動,type == ViewCompat.TYPE_NON_TOUCH時表示由非觸摸產生的滑動,好比慣性形成的滑動就是非觸摸產生的.

所以咱們在滑動過程當中能夠經過type判斷當前滑動是否爲慣性滑動.

public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
    if (type == ViewCompat.TYPE_TOUCH) { // scroll
        
    } else { // fling
        
    }
}
複製代碼

最後,咱們須要在停滑動時,若是列表發生了越界偏移,則須要把列表彈回原位,這裏經過ValueAnimator動畫實現便可.

代碼實現

  • 彈性滑動和慣性滑動過程當中須要一些參數控制滑動效果,如最大的滑動距離,慣性滑動的最小速度,滑動的阻尼因子等,所以咱們須要定義一個接口,和'Behavior`綁定的子View必須實現該接口,接口定義請查看IOverScrollCallback,默認實現爲SimpleOverScrollCallback.

  • 自定義Behavior彈性滑動和慣性滑動,基類爲BaseOverScrollBehavior,控制垂直滾動OverScrollVerticalBehavior,控制水平滾動OverScrollHorizontalBehavior

  • 讓NestedScrolling滑動控件(如RecyclerView,NestedScrollView等)實現IOverScrollCallback,提供相關滑動參數,這裏以OverScrollScrollView控件爲例,代碼請查看OverScrollScrollView.

效果(佈局相關:NestedScrollFragmentlayout_scrollview.xml):

nested over-scroll

項目地址OverScroll

多謝支持個人github項目>>>OverScroll

相關文章
相關標籤/搜索