CoordinatorLayout是FrameLayout的子类
它主要的两个作用:
在使用它的时候,需要依赖:
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有两个重要的概念:
然而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"
android:minHeight="10dp"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
android:minHeight="10dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
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:表示状态栏的“前景色”
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会向上滚动,所以就需要消耗部分滚动的距离
转载自:
http://zhuhf.tech/2017/02/09/CoordinatorLayout/