首页 > 学院 > 开发设计 > 正文

CoordinatorLayout补齐资料篇

2019-11-09 15:41:02
字体:
来源:转载
供稿:网友

CoordinatorLayout补齐资料篇

前言

CoordinatorLayout 是在 Google I/O 2015 提出来的 Material Design 组件,一并发布的有 AppbarLayout ,  CollapsingToolbarLayout  NestedScrollView  等一系列灵活处理动态交互的 Materail Design 控件。 很早就想对 Coordinator  做一次全面的分析,因为懒,一直都没动手,以至于这一篇拖了将近两年才开始写。

组件

工欲善其事,必先利其器。先从官网入手,熟悉一下这一控件的大致功能。

CoordinatorLayout

官网介绍:

这里写图片描述 大致意思就是:

CoordinatorLayout 是一个加强版的 FrameLayout。

主要有两个用途:

作为顶层应用的装饰或者 Chrome 布局作为容器来协调一个或多个子 view 的特定交互

为子View指定 Behaviors 可以实现子 view 与父 view 或者是子 view 相互间的交互。

CoordinatorLayout 的子 view 还可以通过 anchor 来制定位置基准对象。

系统给一些控件提供了默认的 Behavior,处理了相关的联动动态效果,我们也可以自定义 Behavior,注解 DefaultBehavior。关于如何自定义 Behavior,在后面我会着重提到。

AppBarLayout

再来看看官网对AppBarLayout的介绍:

这里写图片描述

大致意思就是:

AppBarLayout是一个竖直的 LinearLayout,而且实现了很多 Material Design 特性并且能够响应滑动事件。

AppBarLayout 最好是 CoordinatorLayout 的直接子 view,如果是在其他 ViewGroup 中,可能会丧失很多特效。

AppBarLayout 的子 view 需要设置 app:layout_scrollFlags,或者是在代码中调用 setScrollFlags(int) 设置这个属性。

AppBarLayout的兄弟节点最好是可以滚动的,需要指定 behavior 属性为 AppBarLayout.ScrollingViewBehavior (可以使用一个内置的 string 表示这个默认的实例@string/appbar_scrolling_view_behavior)。

看来 AppBarLayout 内容比较多啊,来看看 Google 给出的讲解性伪代码。

这里写图片描述

好了,一下子都看清楚了。

CollapsingToolbarLayout

还是来看官方 Api:

这里写图片描述

意思是:

CollapsingToolbarLayout 是实现了折叠工具栏效果 Toolbar 的包装器,被作为 AppBarLayout 的直接子 View。

折叠标题 CollapsingTitle:

当布局完全显示是,标题会变大;当布局上滑滑出屏幕,标题会逐渐变小。可以通过 setTitle()来设置标题,通过 collapsedTextAppearance() 和 expandedTextAppearance() 方法来调整外观属性。

沉浸式内容 Content scrim

ToolBar被折叠到顶部固定时候的背景,你可以调用setContentScrim(Drawable)方法改变背景或者 在属性中使用 app:contentScrim=”?attr/colorPRimary” 来改变背景。

沉浸式状态栏 Status bar scrim

当滚动到一定值时显示或隐藏状态栏沉浸式效果,通过 setStatusBarScrim() 方法来设置,仅在 Android5.0 以上并且设置了适应窗口 (fitsSystemWindows=”true”) 时有用。

视差滚动 Parallax scrolling children

CollapsingToolbarLayout 可以选择视差滚动的方式来滚动,滚动方式有 parallax 和 pin。设置属性 layout_collapseParallaxMultiplier 改变,视差系数在 0-1 之间。

顶部悬浮 CollapseMode

子试图的折叠模式,parallax 和 pin。

pin:固定模式,滑动时也固定大小,直到定位在最终位置

parallax:视差模式,在折叠的时候有视差折叠效果,滑动时伸缩

Behavior

CoordinatorLayout 协调子 View 的响应,响应的核心是 Behavior,来看一下介绍:

这里写图片描述

一个 Behavior 实现了用户的一个或者多个交互行为,它们可能包括拖拽、滑动、快滑或者其他一些手势。

Behavior 是一个顶层抽象类,其他的一些具体行为的Behavior 都是继承自这个类。它提供了几个重要的方法:

layoutDependsOnonDependentViewChangedonStartNestedScrollonNestedPreScrollonNestedScrollonStopNestedScrollonNestedScrollAcceptedonNestedPreFlingonStartNestedScrollonLayoutChild

解释一下上面几个方法和它们的调用时机:

/** * 表示是否给应用了 Behavior 的 View 指定一个依赖的布局,通常,当被依赖的 View (参照物)布局发生变化时 * CoordinatorLayout都会协调依赖的 View(跟随物)立即变化,联动效果不会被受 CoordinatorLayout 子 View 的顺序影响。 * @param parent * @param child 绑定behavior 的View * @param dependency 依赖的view * @return 如果child 是依赖的指定的View 返回true,否则返回false */ @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return super.layoutDependsOn(parent, child, dependency); } /** * 当被依赖的View 状态(如:位置、大小)发生变化时,这个方法被调用 * @param parent * @param child * @param dependency * @return */ @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { return super.onDependentViewChanged(parent, child, dependency); } /** * 当coordinatorLayout 的子View试图开始嵌套滑动的时候被调用。当返回值为true的时候表明 * coordinatorLayout 充当nested scroll parent 处理这次滑动,需要注意的是只有当返回值为true * 的时候,Behavior 才能收到后面的一些nested scroll 事件回调(如:onNestedPreScroll、onNestedScroll等) * 这个方法有个重要的参数nestedScrollAxes,表明处理的滑动的方向。 * * @param coordinatorLayout 和Behavior 绑定的View的父CoordinatorLayout * @param child 和Behavior 绑定的View * @param directTargetChild * @param target * @param nestedScrollAxes 嵌套滑动 应用的滑动方向,看 {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}, * {@link ViewCompat#SCROLL_AXIS_VERTICAL} * @return */ @Override public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) { return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); } /** * 嵌套滚动发生之前被调用 * 在nested scroll child 消费掉自己的滚动距离之前,嵌套滚动每次被nested scroll child * 更新都会调用onNestedPreScroll。注意有个重要的参数consumed,可以修改这个数组表示你消费 * 了多少距离。假设用户滑动了100px,child 做了90px 的位移,你需要把 consumed[1]的值改成90, * 这样coordinatorLayout就能知道只处理剩下的10px的滚动。 * @param coordinatorLayout * @param child * @param target * @param dx 用户水平方向的滚动距离 * @param dy 用户竖直方向的滚动距离 * @param consumed */ @Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) { super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); } /** * 进行嵌套滚动时被调用 * @param coordinatorLayout * @param child * @param target * @param dxConsumed target 已经消费的x方向的距离 * @param dyConsumed target 已经消费的y方向的距离 * @param dxUnconsumed x 方向剩下的滚动距离 * @param dyUnconsumed y 方向剩下的滚动距离 */ @Override public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); } /** * 嵌套滚动结束时被调用,这是一个清除滚动状态等的好时机。 * @param coordinatorLayout * @param child * @param target */ @Override public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) { super.onStopNestedScroll(coordinatorLayout, child, target); } /** * onStartNestedScroll返回true才会触发这个方法,接受滚动处理后回调,可以在这个 * 方法里做一些准备工作,如一些状态的重置等。 * @param coordinatorLayout * @param child * @param directTargetChild * @param target * @param nestedScrollAxes */ @Override public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) { super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); } /** * 用户松开手指并且会发生惯性动作之前调用,参数提供了速度信息,可以根据这些速度信息 * 决定最终状态,比如滚动Header,是让Header处于展开状态还是折叠状态。返回true 表 * 示消费了fling. * * @param coordinatorLayout * @param child * @param target * @param velocityX x 方向的速度 * @param velocityY y 方向的速度 * @return */ @Override public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) { return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); } //可以重写这个方法对子View 进行重新布局 @Override public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) { return super.onLayoutChild(parent, child, layoutDirection); }

基于上面讲的一些  Behavior 的不同回调,Google封装了一些组件使用的 Behavior,比如 AppbarLayout 内部的 Behavior 专门用来协调 AppbarLayout 与可滚动的 View(NestedScrollView,RecyclerView)的,FloatActionButton 内部的 Behavior 协调和 Snackbar 的关系,保证 Snackbar 弹出的时候不被 FAB 遮挡。知道了这些,我们还可以自定义 Behavior。

Behavior 作用于CoordinatorLayout 子 View 的顺序无关性是指根据依赖关系, CoordinatorLayout 中维护了一个mDependencySortedChildren 列表,里面含有所有的子 View,按依赖关系排序,被依赖者排在前面,会在每次测量前重新排序,确保处理的顺序是 被依赖的 View 会先被 measure 和 layout

自定义 Behavior 的方式主要有两种:

第一种是通过监听一个 View 的状态,如位置、大小的变化,来改变其他 View 的行为,这种只需要重写两个方法就可以了,分别是 layoutDependsOn 和 onDependentViewChanged,layoutDependsOn 方法判断是指定依赖的 View 时,返回 true。在 onDependentViewChanged 里,被依赖的 View需要作出行为动作来响应。

第二种就是重写 onStartNestedScroll、onNestedPreScrol、onNestedscroll 等一系列方法。

通过依赖的监听,回调了上述方法。如果 CoordinatorLayout 内的 A 依赖 B,在 B 的大小位置等状态改变的时候,A 可以监听到,并作出响应。

CoordinatorLayout 本身注册了两种监听器,ViewTreeObserver.OnPreDrawListener 和OnHierarchyChangeListener,一种是在绘制的之前进行回调,一种是在子 View 的层级结构发生变化的时候回调,有这两种监听就可以在接受到被依赖的 View 的变化了。

CoordinatorLayout 虽然继承自 ViewGroup,但其更像是 FrameLayout,不能用 RelativeLayout 的layout_below等属性来控制相对位置,所以控件确定上下的位置需要使用其他方式。

一种是:在控件高度不变的情况下,利用margin来定位。比如头部 Header 高度恒定为40dp,那么想要一个控件放在 Header 下方,就设置这个控件的 marginTop 为 40dp 就好了。这种处理方式比较局限。

另一种是:利用 Behavior 提供的 onLayoutChild 方法重写控件的 layout 过程。

未完待续,会写个例子补充

参考资料:

Md系列3、CoordinatorLayout 里 Toobar和TabLayout等发生的一系列故事 自定义Behavior的艺术探索-仿UC浏览器主页


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表