嵌套滑動機制-仿知乎日報效果

處理滑動的基本思路都是基於View事件傳遞機制,嵌套滑動(NestedScroll)機制也不例外。android

大量控件都實現了嵌套滑動機制,有不少源碼能夠學習。例如RecyclerViewSwipeRefreshLayout,下面提供一個使用嵌套滑動機制來實現仿知乎日報效果Demo,效果以下:git

enter image description here

嵌套滑動機制原理(NestedScroll)

假設兩個View分別爲ParentChild,前者是後者的佈局容器。github

通常說來,Child處於主動地位,其滑動將帶動Parent作跟隨滑動。在嵌套滑動中優先對Child的事件作出響應。segmentfault

Parent雖然處於被動跟隨地位,也要有必定自主權,可以決定本身參與Child發起的嵌套滑動事件。Parent經常是Child所處的佈局容器,繼承ViewGroup類。數組

嵌套滑動機制的使用十分簡單,只須要ParentChild中分別實現兩個接口。app

1.Child實現NestedScrollingChild接口。
實際上該接口的實現能夠徹底使用幫助類NestedScrollingChildHelper來代理,惟一要處理的是如何在Child的觸摸事件方法中調用該接口的方法。佈局

例如使用下列方法開啓/關閉嵌套滑動學習

  • startNestedScroll(int axes);啓動嵌套滑動。spa

  • stopNestedScroll();中止嵌套滑動。代理

同時嵌套滑動是一個微小增量過程,使用下列方法在每一步滑動中向Parent分發消息。

  • dispatchNestedPreScroll

  • dispatchNestedScroll()

2.Parent實現NestedScrollingParent接口。

該接口中若干方法的實現與具體業務邏輯有關,應該用戶本身實現。以下列方法判斷父佈局控件是否要參與子控件發起的嵌套滑動。

  • onStartNestedScroll 判斷是否參與嵌套滑動。

  • onNestedScrollAccepted 肯定參與嵌套滑動後將執行,可用做配置初始化。

在嵌套滑動過程當中,要處理Child所分發的步進事件。

  • onNestedPreScroll 響應ChilddispatchNestedPreScroll方法。

  • onNestedScroll 響應ChilddispatchNestedScroll方法。

在一次嵌套滑動事件中,ParentChild處於問答式交互。以用戶觸摸事件開端,Child控件會詢問Parent是否參與滑動;然後在每一次增量滑動先後,兩者都會溝通。具體以下圖

enter image description here

源碼解析

1.若是在觸摸事件中調用了startNestedScroll方法,實際是由NestedScrollingChildHelper類實現

public boolean startNestedScroll(int axes) {

    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;
}

子控件會不斷上溯尋找可以響應嵌套滑動事件的父容器,而且會調用父控件的onStartNestedScroll方法和onNestedScrollAccepted方法。

2.再看子控件在滑動前的分發事件方法dispatchNestedPreScroll

  • 參數dxdy應該是子控件中觸摸事件滑動距離,能夠計算出來。

  • consumed是一個數組,應該以零值傳入,表示父控件是否消費了滑動事件。在父控件調用後,若是consumed不爲0,整個方法返回true,表示父控件消費了滑動事件。

  • offsetInWindow表示執行先後子控件在屏幕上的偏移量。

public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
    //若是開啓嵌套滑動,且父控件存在,且滑動有效
    mView.getLocationInWindow(offsetInWindow);
    int startX = offsetInWindow[0];
    int startY = offsetInWindow[1];
    
    consumed[0] = 0;
    consumed[1] = 0;  
    ViewParentCompat.onNestedPreScroll(
    mNestedScrollingParent, mView, dx, dy, consumed);
    
    mView.getLocationInWindow(offsetInWindow);
    offsetInWindow[0] -= startX;
    offsetInWindow[1] -= startY;
b}

Action Bar的徹底透明

Action Bar的佈局以下

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:background="?attr/colorPrimaryDark"//須要更改
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:popupTheme="@style/AppTheme.PopupOverlay"/>

要作到徹底透明,要更改如下屬性
1.AppBarLayout背景的透明度
2.Toolbar背景透明度
3.Toolbar背景度
4.此外Toolbar的背景配置不能使用android:background="?attr/colorPrimary",必需要換一種近似顏色。

mAppBarLayout.getBackground().setAlpha(alpha);
mToolbar.getBackground().setAlpha(alpha);
mToolbar.setAlpha(alpha);
  • 此外爲了使得Toolbar不遮擋子控件的內容,應該去掉子控件的behave屬性。

  • 爲了去掉AppBar遺留的陰影,應使用以下方法

mAppBarLayout.setElevation(0.1f);

該值越大,陰影效果越明顯,但不能設置爲0,設置一個較小值便可近似無陰影。

參考文獻

相關文章
相關標籤/搜索