Android学习之CoordinatorLayout

介绍:

CoordinatorLayout是FrameLayout的子类
它主要的两个作用:

  • 作为应用的顶层布局
  • 作为协调子view之间交互的容器

在使用它的时候,需要依赖:

compile'com.android.support:design:25.1.0'

AppBarLayout是一个垂直的LinearLayout,它主要是为了实现 “Material Design” 风格的标题栏的特性,比如:滚动。

对AppBarLayout的子View,应该提供一个滚动的行为,通过setScrollFlags(int)设置
在官网汇总,有这样一句话:

This view depends heavily on being used as a direct child within a CoordinatorLayout. If you use AppBarLayout within a different ViewGroup, most of it’s functionality will not work.
也就是说AppBarLayout应该作为CoordinatorLauout的直接子View,如果不用CoodinatorLayout作为ViewGroup你,那么很多特性会失效

如果不使用CoordinatorLayout,实现这个效果的方案有两种:
1,自己处理触摸时间的分发,来改变标题栏的位置
2,使用 support.v4 引入的 NestedScrolling 机制。

这里,NestedScroolling有两个重要的概念:

  • NestedScrollingParent
  • NestedScrollingChild

然而CoordinatorLayout已经实现了NestedScrollingParent接口,所以我们配合一个实现了NestedScrolllingChild接口的CIew就可以轻松实现上面的效果

"1.0" encoding="utf-8"?>
.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"           
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    .support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        "match_parent"
            android:layout_height="wrap_content"
            android:text="标题"
            android:textSize="20sp"
            android:gravity="center"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:textColor="@android:color/white"
            android:background="@color/colorPrimary"
            app:layout_scrollFlags="scroll"/>
    .support.design.widget.AppBarLayout>

    .support.v4.widget.NestedScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        "@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="30sp"
            android:gravity="center_horizontal"/>
    .support.v4.widget.NestedScrollView>
.support.design.widget.CoordinatorLayout>

在CoordinatorLayout中可滚动的视图需要设置以下属性:用来处理NestedScrollView与AppBarLayout的关系

app:layout_behavior=”@string/appbar_scrolling_view_behavior”

AppBarLayout中的Vew,如果想要滚动到屏幕外面,必须设置:

app:layout_scrollFlags="scroll"
  • scroll:隐藏的时候,先整体向上滚动,直到AppBarLayout完全隐藏,再开始滚动ScrollingView;显示的时候,直到ScrollingView顶部完全出现后,再开始滚动AppBarLayout

    除了 scroll,还有下面几个取值,这些属性都必须与 scroll 一起使用 “|” 运算符。
  • enterAlways:与scroll类似(scroll | enterAlways),只不过向下滚动先显示AppBarLayout到完全,再滚动Scrolling View
  • enterAlwaysCollapsed:需要额enterAlways一起使用(scroll | enterAlways | enterAlwaysCollapsed),和enterAlways不一样的是,不会显示AppBarLayout到完全再滚动Scrolling View,而是先滚动AppBarLayout到最小高度,再滚动Scrolling View,最后载滚动AppBarLayout到完全显示
    注意:需要定义View的最小高度(minHeight)才有效果:
android:minHeight="10dp"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
  • exitUntilCollapsed:定义了AppBarLayout消失的规则。发生向上滚动事件时,AppBarLayout向上滚动退出直最小高度(minHeigth),然后Srcolling View开始滚动。也就是AppBarLayout不会完全退出屏幕。
android:minHeight="10dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"

CoordinatorLayout与CollapsingToolbarLayout

CollapdingToolbarLayout继承自FrameLayout,它是用来实现Toolbar的折叠效果,一般它的直接子view是Toolbar,当然也可以是其他类型的view
如果不适用Toolbar,有些效果没有办法直接实现。比如下图中的“My files”文字在折叠和展开的时候,有一个过渡效果:

也就是CollapsingToolbarLayout设置title的相关方法无效,比如: setTitle、setCollapsedTitleTextColor、setExpandedTitleGravity等
另外,exitUntilCollapsed属性也会失效,即使设置了minHeight,所以官方也说明了它是为了配合Toolbar而设计得

"1.0" encoding="utf-8"?>
.support.design.widget.CoordinatorLayout     
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    .support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="150dp">
        .support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbarLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="@color/colorPrimary"
            app:title="标题"
            app:expandedTitleMarginEnd="10dp"

            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed">
             .support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="50dp"
            app:layout_collapseMode="pin"
            android:minHeight="10dp"
            android:background="@color/colorPrimary"
            >

            "wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/ic_launcher_round"
                />

        .support.v7.widget.Toolbar>
        .support.design.widget.CollapsingToolbarLayout>
    .support.design.widget.AppBarLayout>

    .support.v4.widget.NestedScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        "@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="30sp"
            android:gravity="center_horizontal"/>
    .support.v4.widget.NestedScrollView>
.support.design.widget.CoordinatorLayout>

app:layout_collapseMode=”pin” 确保 CollapsingToolbarLayout 折叠完成之前,Toolbar 一直固定在顶部不动。

修改CollapsingTollbarLayout的layout-crollFlags:app:layout_scrollFlags="scroll|exitUntilCollapsed"

parallax(视差)
layout_collapseMode 除了使用pin固定住View,还可以使用parallax,视差的意思是:移动过程中两个view的位置产生了一定的视觉差异

"1.0" encoding="utf-8"?>
.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    .support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="150dp">
        .support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbarLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="@color/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
            "match_parent"
                android:layout_height="match_parent"
                android:src="@drawable/bg"
                android:scaleType="centerCrop"
                app:layout_collapseParallaxMultiplier="0.9"
                app:layout_collapseMode="parallax"/>
            .support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                app:layout_collapseMode="pin"/>
        .support.design.widget.CollapsingToolbarLayout>
    .support.design.widget.AppBarLayout>

    .support.v4.widget.NestedScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        "@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="30sp"
            android:gravity="center_horizontal"/>
    .support.v4.widget.NestedScrollView>
.support.design.widget.CoordinatorLayout>

还可以设置视差因子:

app:layout_collapseParallaxMultiplier=”0.9”
下图是视差因子为0.1的效果:

这里还有几个属性需要介绍下:

contentScrim:表示 CollapsingToolbarLayout 折叠之后的“前景色”,我们看到 Toolbar 的变色,其实是因为前景色遮挡了而已。
statusBarScrim:表示状态栏的“前景色”

Behavior

CoodinatorLayout并不知道FloatingActionButton和AppBarLayout的工作原理,我们提到过 CoodinatorLayout 实现了 NestedScrollingParent,我们通过一个实现了 NestedScrollingChild 的 scrolling view,就可以轻松的实现:滑动事件的处理与 View 之间的交互。

这其中充当中间桥梁的就是CoordinatorLayout.Behavior,让其知道何时可以滚动,比如FloatingActionButton,查看源码发现它的类注解是这样:

@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
public class FloatingActionButton extends VisibilityAwareImageButton {
    // ...
}

FloatingActionButton.Behavior 的主要作用就是防止被 Snackbar 盖住。

自定义View既可以通过注解指定Behavior,可以在布局XML申明:

app:layout_behavior="具体Behavior的类路径"

原理分析
以 “、CoordinatorLayout 与 AppBarLayout” 中的效果来分析:

1,滑动NestedScrollView会触发CoodinatorLayout的onNestedPreScroll方法
2,onNestedPreScroll方法会查找所有定义了Behavior的View,再通过Behavior的onNestedPreScroll方法去询问每个View需要消耗的距离
(部分源码)

@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
   int xConsumed = 0;
   int yConsumed = 0;
   boolean accepted = false;

   final int childCount = getChildCount();
   for (int i = 0; i < childCount; i++) {
      final View view = getChildAt(i);

      final Behavior viewBehavior = lp.getBehavior();
      if (viewBehavior != null) {
          mTempIntPair[0] = mTempIntPair[1] = 0;
          viewBehavior.onNestedPreScroll(this, view, target, dx, dy, mTempIntPair);

          xConsumed = dx > 0 ? Math.max(xConsumed, mTempIntPair[0])
               : Math.min(xConsumed, mTempIntPair[0]);
          yConsumed = dy > 0 ? Math.max(yConsumed, mTempIntPair[1])
               : Math.min(yConsumed, mTempIntPair[1]);

          accepted = true;
      }
   }

   consumed[0] = xConsumed;
   consumed[1] = yConsumed;
}

3,NestedScrollView 的 Behavior:AppBarLayout.ScrollingViewBehavor,它的 onNestedPreScroll 没有做任何实现;

4,那么剩下的就是 AppBarLayout 的 Behavior:AppBarLayout.Behavior

@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
     View target, int dx, int dy, int[] consumed) {
   if (dy != 0 && !mSkipNestedPreScroll) {
      int min, max;
      if (dy < 0) {
          // We're scrolling down
          min = -child.getTotalScrollRange();
          max = min + child.getDownNestedPreScrollRange();
      } else {
          // We're scrolling up
          min = -child.getUpNestedPreScrollRange();
          max = 0;
      }
      consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
   }
}

我们滑动NestedScollView的时候,AppBarLayout会向上滚动,所以就需要消耗部分滚动的距离

关于自定义behavior等自己研究研究事件分发再来研究这个

转载自:
http://zhuhf.tech/2017/02/09/CoordinatorLayout/

你可能感兴趣的:(android)