CoordinatorLayout系列(三)AppBarLayout之layout_scrollFlags

系列文章:
CoordinatorLayout系列(一):Behavior
CoordinatorLayout系列(二)AppBarLayout
CoordinatorLayout系列(三)AppBarLayout之layout_scrollFlags
CoordinatorLayout系列(四)CollapsingToolbarLayout
CoordinatorLayout系列(五)例子

在上一篇文章裏面CoordinatorLayout系列(二)AppBarLayout裏面講到了AppBarLayout的使用,這一篇準備講一下AppBarLayout是怎麼通過layout_scrollFlags控制child view的滑動,以及各種flag產生的效果。

一、Flags種類

Flags在代碼裏面具體位於AppBarLayout的LayoutParams中:

public LayoutParams(Context c, AttributeSet attrs) {
      super(c, attrs);
      TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_Layout);
      scrollFlags = a.getInt(R.styleable.AppBarLayout_Layout_layout_scrollFlags, 0);
      if (a.hasValue(R.styleable.AppBarLayout_Layout_layout_scrollInterpolator)) {
        int resId = a.getResourceId(R.styleable.AppBarLayout_Layout_layout_scrollInterpolator, 0);
        scrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator(c, resId);
      }
      a.recycle();
    }

Flags分爲SCROLL_FLAG_SCROLLSCROLL_FLAG_EXIT_UNTIL_COLLAPSEDSCROLL_FLAG_ENTER_ALWAYSSCROLL_FLAG_ENTER_ALWAYS_COLLAPSEDSCROLL_FLAG_SNAPSCROLL_FLAG_SNAP_MARGINS這麼六種,這裏只介紹前四種

二、Flags的效果

  • scroll
    所有需要滑動的效果,都要跟上這個flag,否則Toolbar根本不能滑動。

  • scroll | enterAlways
    這個效果很常見了,就是ToolBar跟隨scrollview進行滑入滑出
    在這裏插入圖片描述

  • scroll|enterAlways|enterAlwaysCollapsed
    在這裏插入圖片描述
    這個效果要設置好ToolBar的minHeight和真正的Height

<androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:minHeight="?attr/actionBarSize"
            android:layout_height="200dp"
            android:background="@color/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            app:title="AppBarLayoutExample"
            app:titleTextAppearance="@style/ToolbarTitleStyle"></androidx.appcompat.widget.Toolbar>

滑動的機制是,當向上滑動時,和只設置enterAlwalys一樣,當向下滑動時,先滑動minHeight這麼多,剩下的向下滑動事件由scrollView消費,等到scrollview滑到頂部時,再向下滑動,這時ToolBar才滑動剩餘的部分。

  • scroll|exitUntilCollapsed
    在這裏插入圖片描述
    這組標誌位也要設置minHeight
<androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:minHeight="20dp"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:title="AppBarLayoutExample"
            app:titleTextAppearance="@style/ToolbarTitleStyle"></androidx.appcompat.widget.Toolbar>

滑動的機制是向上滑動時,不會全部滑出,會minHeight這麼多,當向下滑動時,要等到scrollview滑到頂部,才往下滑入。

三、滑動的具體實現

看到上面幾種滑動,那麼我們就要思考到,CoordinatorLayout是如何實現上面的效果呢?
其實具體的實現效果還是比較複雜的,我在這裏只是講一下大概的實現方式,要全部講清楚還要深入到view的內部細節才能完全講清。
在上一篇中講到,CoordinatorLayout和Recyclerview是基於NestedScroll機制實現聯動效果的,Move事件的傳遞順序是這樣的:
1、首先,點擊事件傳遞到Recyclerview,進入onTouchEvent方法
2、由於NestedScroll機制,需要先詢問parent,是否消費事件,進入CoordinatorLayout.onNestedPreScroll,這裏面就會由childview的Behavior代理消費事件,進入到AppBarLayout.BaseBehavior.onNestedPreScroll
3、parent消費完一部分之後,就由Recyclerview自己消費了,如果滑到頂部的話,那麼肯定還有一部分沒消費完,因此還要parent消費剩下的,進入到CoordinatorLayout.onNestedScroll
4、和2相同,仍然由AppBarLayout.BaseBehavior.onNestedScroll代理消費

上面就是整個的事件消費流程,2、4兩點是很重要的,因爲涉及到AppBarLayout控制child view的滑動行爲,以onNestedPreScroll爲例,這個行爲代表着當我們準備讓Recyclerview滑動時,最終是被AppBarLayout消費掉了,典型的enterAlwayls這個flag的行爲

public void onNestedPreScroll(
        CoordinatorLayout coordinatorLayout,
        @NonNull T child,
        View target,
        int dx,
        int dy,
        int[] consumed,
        int type) {
      if (dy != 0) {
        int min;
        int max;
        if (dy < 0) {
          // We're scrolling down
          //計算AppBarLayout整個的可以滑動的高度,minHeight是不參與滑動距離的
          min = -child.getTotalScrollRange();
          //getDownNestedPreScrollRange會根據這個階段計算出最大可以滑動的距離
          max = min + child.getDownNestedPreScrollRange();
        } else {
          // We're scrolling up
          min = -child.getUpNestedPreScrollRange();
          max = 0;
        }
        if (min != max) {
        //當可以滑動的最小距離不等於最大距離時,說明此時AppBarLayout是可以滑動的,因此就讓AppBarLayout消費掉dy事件的一部分
          consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
        }
      }
      if (child.isLiftOnScroll()) {
        child.setLiftedState(child.shouldLift(target));
      }
    }

上面最重要的點就是min和max的計算方法,min就是最小可以滑動的距離,max就是最大可以滑動的距離,這兩個值其實是定值,和child view的高度,flags相關,計算的過程就是和flags相關的,因此不同的flags就能產生不同的效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章