談CoordinatorLayout.Behavior

Behavior 是 CoordinatorLayout爲其 子視圖 提供的一種交互行爲插件。
它實現了用戶能夠操做子視圖的一種或多種交互行爲,包括包括拖拽,Fling(滑翔)或任何其餘手勢。java

直接子類

直接子類.png

CoordinatorLayout、AppBarLayout、SwipeDismissBehavior等使用方法

自定義

自定義Behavior,這裏分爲兩類:swift

  • Dependent機制
    layoutDependsOnonDependentViewChanged做爲一組。Dependent機制最多見的案例就是 FloatingActionButtonSnackBar的交互行爲。如圖
  • Nested機制
    onStartNestedScrollonNestedScroll做爲一組。Nested機制要求 CoordinatorLayout包含了一個實現了 NestedScrollingChild 接口的滾動視圖控件,好比v7包中的 RecyclerView,設置 Behavior屬性的Child View會隨着這個控件的滾動而發生變化。如圖

以下只標註了Behavior部分方法。開發者能夠根據自身業務需求有選擇的複寫。app

部分方法ide

  • layoutDependsOn

    此方法用於判斷給定的View和同級View是否做爲佈局依賴關係。
    /** * @param parent * @param child 給定的View,即應用了layout_behavior的View * @param dependency 任何與child同級的View * @return 若是返回true,那麼parent將作兩件事: * 1.將忽略View的順序,老是先去佈局dependency,以後佈局child。 * 2.當dependency視圖的佈局或位置發生改變時,調用onDependentViewChanged方法。 */
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
      //判斷dependency是不是須要的依賴項,若是是,則返回true
      return false;
    }複製代碼
  • onDependentViewChanged

    此方法用於對依賴視圖的改變作出響應。開發者能夠複寫此方法從而改變child的大小和位置,並返回true。
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
      //處理child的位置或大小,並返回true
      return true;
    }複製代碼
  • onStartNestedScroll

    此方法用於判斷是否進行嵌套滾動。與CoordinatorLayout的任何直接子項相關聯的任何Behavior均可以響應此事件。若是返回true,代表CoordinatorLayout應該充當此滾動的嵌套滾動父項。只有返回true,纔會執行後續的嵌套滾動方法。
    /** * @param coordinatorLayout * @param child 關聯Behavior的CoordinatorLayout的子View * @param directTargetChild CoordinatorLayout的子View或包含嵌套滾動操做的View。好比RecycleView外層的RelativeLayout * @param target 嵌套滾動的View * @param nestedScrollAxes 嵌套滾動的座標軸。SCROLL_AXIS_HORIZONTAL, SCROLL_AXIS_VERTICAL * @return */
    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
      return false;
    }複製代碼
  • onNestedScroll

    此方法用於處理嵌套滾動。
    每次嵌套滾動由嵌套滾動子元素更新時,onNestedScroll被調用,滾動的消費組件和未消費組件以像素提供。
    /** * * @param coordinatorLayout * @param child * @param target * @param dxConsumed 水平方向滾動增量, * @param dyConsumed 垂直方向滾動增量,若是大於0,手指上滑中;若是小於0,手指下滑中。 * @param dxUnconsumed 同dyUnconsumed描述 * @param dyUnconsumed 正常狀況下,始終爲0,當View處於最頂部或最底部,用戶仍然強制下滑或上滑時,dy則不爲0 */
    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
      if (dyConsumed > 0 && dyUnconsumed == 0) {
          System.out.println("上滑中。。。");
      }
      if (dyConsumed == 0 && dyUnconsumed > 0) {
          System.out.println("到邊界了還在上滑。。。");
      }
      if (dyConsumed < 0 && dyUnconsumed == 0) {
          System.out.println("下滑中。。。");
      }
      if (dyConsumed == 0 && dyUnconsumed < 0) {
          System.out.println("到邊界了,還在下滑。。。");
      }
    }複製代碼

原理分析

  1. Behavior所屬

    經過查看CoordinatorLayout源碼能夠知道,它有一個內部類LayoutParams,用於存儲CoordinatorLayout的全部子View佈局參數,其中Behavior也是LayoutParams的一個屬性值。所以和文章開始的描述保持了一致,Behavior只能設置到CoordinatorLayout子View上。函數

    /** * Parameters describing the desired layout for a child of a {@link CoordinatorLayout}. */
    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
    /** * A {@link Behavior} that the child view should obey. */
    Behavior mBehavior;
    //省略n多代碼
    }複製代碼
  2. 設置Behavior兩種方式

    • app:layout_behavior佈局屬性
      在佈局中設置,值爲自定義 Behavior類的名字字符串(包含路徑),有兩種寫法,包含包名的全路徑和以」.」開頭的省略項目包名的路徑。

      app:layout_behavior="com.yolo.myapplication.MyBehavior"
      app:layout_behavior="@string/appbar_scrolling_view_behavior"佈局

    • @CoordinatorLayout.DefaultBehavior類註解
      在須要使用 Behavior的控件源碼定義中添加該註解,而後經過反射機制獲取。系統的 AppBarLayout、 FloatingActionButton都採用了這種方式,因此無需在佈局中重複設置。
  3. Behavior實例化

    在LayoutParams構造方法中,調用了parseBehavior(context, attrs, a.getString( R.styleable.CoordinatorLayout_Layout_layout_behavior))方法。判斷名稱,而後經過反射機制實例化Behavior。從而回調Behaivor的其餘方法。ui


    注意:在自定義 Behavior時,必定要重寫第二個帶參數的構造函數,不然這個 Behavior是不會起做用的。this

    static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
     if (TextUtils.isEmpty(name)) {
         return null;
     }
    
     final String fullName;
     if (name.startsWith(".")) {
                 //若是behavior的值以 . 開頭,則自動補全包名信息
         // Relative to the app package. Prepend the app package name.
         fullName = context.getPackageName() + name;
     } else if (name.indexOf('.') >= 0) {
         // Fully qualified package name.
         fullName = name;
     } else {
         // Assume stock behavior in this package (if we have one)
         fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
                 ? (WIDGET_PACKAGE_NAME + '.' + name)
                 : name;
     }
    
     try {
         Map<String, Constructor<Behavior>> constructors = sConstructors.get();
         if (constructors == null) {
             constructors = new HashMap<>();
             sConstructors.set(constructors);
         }
         Constructor<Behavior> c = constructors.get(fullName);
         if (c == null) {
                         //經過反射實例化Behavior
             final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
                     context.getClassLoader());
             c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
             c.setAccessible(true);
             constructors.put(fullName, c);
         }
         return c.newInstance(context, attrs);
     } catch (Exception e) {
         throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
     }
    }複製代碼

推薦閱讀

Android 一步一步分析Behavior
(譯)掌握 Coordinator Layoutspa

相關文章
相關標籤/搜索