CoordinatorLayout是Android官方在Design包提供的控件,来自官方的解释是:
CoordinatorLayout is a super-powered FrameLayout
它主要用于两个方面:
CoordinatorLayout为我们提供了一个叫做Behavior的东西,我们基本上的复杂交互都是使用Behavior来协调完成。
先看看本文的最终成果:
这里我选择的是26.0.2版本,至于为什么选择这个,是因为我使用的最多的就是这个版本,暂时没有发现一些其他的什么问题。
dependencies{
implementation 'com.android.support:design:26.0.2'
}
自从 Android Studio3.0 以后,基本上都是改用了 implementation,而不是使用 compile ,主要原因是使用 implementation 不会产生依赖传递,这对于我们来说,减少了很多麻烦,比方说以前解决的重复依赖版本的问题。
很简单的写了一下,现在只要是当做简单的FrameLayout来使用,内部只放了一个TextView,详情看以下代码:
我们可以运行起来看看效果
CoordinatorLayout的第一点作用就已经展现出来了,在我们要讲解它的第二点功能之前,我们需要准备一些预备知识。
AppBarLayout是一个垂直的 LinearLayout, 它实现了很多在material designs设计中提出的概念性交互功能,也就是【滚动手势】。
AppBarLayout也是design包中的一个控件,所以在这里就不需要再添加依赖了。我们可以通过给它的子View进行setScrollFlags(int)或者直接在xml中增加属性app:layout_scrollFlags来设置它子View的滚动行为。
需要注意的是,AppBarLayout需要配合CoordinatorLayout进行使用,如果只是放到普通的ViewGroup中使用的话将无法实现它的效果。
随着内容的滑动,ToolBar也跟随着滑动,详情看效果图:
Behavior这个名词看着陌生,但事实上我们经常简单,如:app:layout_behavior="@string/appbar_scrolling_view_behavior",这个一般是出现在使用Android Stuido创建Activty的时候自动创建的,他的作用是让使用这个属性的View在appbar下面滚动。而Behavior主要的使用方式其实是通过反射来实现的,我们在layout_behavior中并没有直接进行引用,而是写了包名+类名,所以Behavior是不能够被混淆的。
前面讲了那么都得概念,关于CollapsingToolbarLayout的使用在网络上已经写得烂大街了,所以在这里也就不做太多的概述,关于layout_scrollFlags的使用和介绍也是一样的,所以我们直接上手代码吧。
下面的小例子是为了实现一个简单的透明度渐变的toolbar和header的移动。先看看效果图吧:
从图中可以看到,我们的标题栏是随着上下滑动而透明度渐变,头像在展开状态下处于居中状态,随着慢慢的滑动合上,头像会改变X轴和Y轴的坐标,最终移动到返回键的旁边。
我们采用的做法是用AppBarLayout包裹一个CollapsingToolbarLayout,CollapsingToolbarLayout中放我们的背景图片,随着滑动收缩和关闭图片,下面是一个NestedScrollView,里面放LinearLayout和一大堆的CardView。
activity_transfer_header中的布局
其中我们CollapsingToolbarLayout的layout_scrollFlags使用的是scroll和exitUntilCollapsed,scroll是必需要设置的,不设置将无法滑动,exitUntilCollapsed可以让CollapsingToolbarLayout退出关闭。
ImageView的layout_collapseMode为parallax,代表ImageView滑动会有一个视差滚动的效果,而视差滚动的比值(layout_collapseParallaxMultiplier)是0.9。需要注意的是CollapsingToolbarLayout内部必需有一个铺满高度的VIew来做参考坐标,自定义Behavior才能够起作用。
layout_tr_content中的内容
好了,我们运行起来看看效果吧:
诶? 有没有发现效果不一样? 我们运行起来的是这样:
而我们效果图中的是这样:
其实只需要在NestedScrollView中加上一个属性behavior_overlapTop,它会向上缩进赋值的高度:
创建TranslucentBehavior继承至CoordinatorLayout.Behavior。我们在Behavior中计算移动的Y轴和总高度的比例,然后计算alpha通道的值,设置Toolbar的背景颜色。
具体代码实现
public class TranslucentBehavior extends CoordinatorLayout.Behavior {
/**标题栏的高度*/
private int mToolbarHeight = 0;
public TranslucentBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, Toolbar child, View dependency) {
return dependency instanceof TextView;
}
/**
* 必须要加上 layout_anchor,对方也要layout_collapseMode才能使用
*/
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, Toolbar child, View dependency) {
// 初始化高度
if (mToolbarHeight == 0) {
mToolbarHeight = child.getBottom() * 2;//为了更慢的
}
//
//计算toolbar从开始移动到最后的百分比
float percent = dependency.getY() / mToolbarHeight;
//百分大于1,直接赋值为1
if (percent >= 1) {
percent = 1f;
}
// 计算alpha通道值
float alpha = percent * 255;
//设置背景颜色
child.setBackgroundColor(Color.argb((int) alpha, 63, 81, 181));
return true;
}
}
在xml中进行引用:
需要注意的是:
来看看效果吧:
完美达成!!
创建TransferHeaderBehavior继承CoordinatorLayout.Behavior,传入泛型是ImageView,这个ImageView就是我们的参考坐标View。
实现原理是:
具体实现看代码:
public class TransferHeaderBehavior extends CoordinatorLayout.Behavior {
/**
* 处于中心时候原始X轴
*/
private int mOriginalHeaderX = 0;
/**
* 处于中心时候原始Y轴
*/
private int mOriginalHeaderY = 0;
public TransferHeaderBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, ImageView child, View dependency) {
return dependency instanceof Toolbar;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, ImageView child, View dependency) {
// 计算X轴坐标
if (mOriginalHeaderX == 0) {
this.mOriginalHeaderX = dependency.getWidth() / 2 - child.getWidth() / 2;
}
// 计算Y轴坐标
if (mOriginalHeaderY == 0) {
mOriginalHeaderY = dependency.getHeight() - child.getHeight();
}
//X轴百分比
float mPercentX = dependency.getY() / mOriginalHeaderX;
if (mPercentX >= 1) {
mPercentX = 1;
}
//Y轴百分比
float mPercentY = dependency.getY() / mOriginalHeaderY;
if (mPercentY >= 1) {
mPercentY = 1;
}
float x = mOriginalHeaderX - mOriginalHeaderX * mPercentX;
if (x <= child.getWidth()) {
x = child.getWidth();
}
// TODO 头像的放大和缩小没做
child.setX(x);
child.setY(mOriginalHeaderY - mOriginalHeaderY * mPercentY);
return true;
}
}
在xml中引用
运行查看效果:
完美达成!!
TransferHeaderBehavior中遗留了一个todo:头像的缩小,这个留着下一章实现。
未完待续、敬请期待!
免为其难的关注一下公众号吧!!