您的位置:首页 > 其它

CoordinatorLayout自定义Behavior的简单总结

2017-02-13 16:11 423 查看

前言

CoordinatorLayout 是 Google 在 Design Support 包中提供的一个十分强大的布局视图,它可以说是Design库这种最重要的控件,虽说本质上类似于 FrameLayout,但是它允许开发者通过指定 Behavior 从而实现各种复杂的 UI 效果

CoordinatorLayout与Behavior介绍

官方对CoordinatorLayout的描述:

CoordinatorLayout is a super-powered FrameLayout.

CoordinatorLayout is intended for two primary use cases:

As a top-level application decor or chrome layout
As a container for a specific interaction with one or more child views


官方对Behavior的描述:

Interaction behavior plugin for child views of CoordinatorLayout.


意思就是CoordinatorLayout是用来协调其子view们之间动作的一个父view,而Behavior就是用来给CoordinatorLayout的子view们实现交互的

注意:

使用Behavior需要注意的是 CoordinatorLayout 子视图的层级关系,如果想在子视图中使用 Behavior 进行控制,那么这个子视图一定是 CoordinatorLayout 的直接孩子,间接子视图是不具有 behavior 属性的,原因当然也很简单,behavior 是 LayoutParams 的一个属性,而间接子视图的 LayoutParams 根本不是 CoordinatorLayout 类型的

Behavior的一些重要回调方法介绍

一般自定义Behavior需要重写的几个重要方法:

layoutDependsOn

/**
* 表示是否给应用了Behavior 的View 指定一个依赖的布局,通常,当依赖的View 布局发生变化时
* 不管被依赖View 的顺序怎样,被依赖的View也会重新布局
* @param parent CoordinatorLayout对象
* @param child 绑定behavior的View
* @param dependency   依赖的view
* @return 如果child 是依赖的指定的View 返回true,否则返回false
*/
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
if (dependency != null && dependency.getId() == R.id.scrolling_header) {
//do something
return true;
}
return false;
}


负责查询该 Behavior 是否依赖于某个视图,如果是则返回 true,那么之后其他操作就会围绕这个依赖视图而进行了

onDependentViewChanged

/**
* 当被依赖的View 状态(如:位置、大小)发生变化时,这个方法被调用
* @param parent CoordinatorLayout对象
* @param child 绑定behavior的View
* @param dependency 依赖的view
* @return 当dependency发生改变,同样child也需要发生改变,这个时候需要返回true
*/
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
final float progress = Math.abs(dependency.getTranslationY() / (dependency.getHeight()));
//do something
return true;
}


这段就是根据依赖视图进行调整的方法,当依赖视图发生变化时,这个方法就会被调用。在这里可以通过当前依赖视图的位移,计算出一个位移因数(取值 0 - 1),用该位移因数来做一些视图的位移,缩放等等的操作

onLayoutChild

//可以重写这个方法对子View 进行重新布局
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
if (lp.height == CoordinatorLayout.LayoutParams.MATCH_PARENT) {
child.layout(0, 0, parent.getWidth(), (int) (parent.getHeight() - getDependencyViewHeight()));
return true;
}
}


负责对被 Behavior 控制的视图进行布局,就是将 ViewGroup 的 onLayout 针对该视图的部分抽出来给 Behavior 处理。我们判断一下如果目标视图高度要填充父视图,我们就自己将其高度减去 Header View 折叠后的高度。为什么要这么做呢?因为 CoodinatorLayout 就是一个 FrameLayout,不像 LinearLayout 一样能自动分配各个 View 的高度,因此我们要自己实现大小控制

onStartNestedScroll

/**
*  当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 (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}


用户按下手指时触发,询问 NSP 是否要处理这次滑动操作,如果返回 true 则表示“我要处理这次滑动”,如果返回 false 则表示“我不 care 你的滑动,你想咋滑就咋滑”,后面的一系列回调函数就不会被调用了。它有一个关键的参数,就是滑动方向,表明了用户是垂直滑动还是水平滑动,本例子只需考虑垂直滑动,因此判断滑动方向为垂直时就处理这次滑动,否则就不 care

onNestedPreScroll

/**
* 嵌套滚动发生之前被调用
* 一般可以用来处理向上滑动时的逻辑
* @param coordinatorLayout
* @param child
* @param target
* @param dx  用户水平方向的滚动距离
* @param dy  用户竖直方向的滚动距离
* @param consumed 消费距离,[0]为水平距离,[1]为垂直距离
*/
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
}


在nested scroll child 消费掉自己的滚动距离之前,嵌套滚动每次被nested scroll child更新都会调用onNestedPreScroll。注意有个重要的参数consumed,可以修改这个数组表示你消费了多少距离。假设用户滑动了100px,child 做了90px 的位移,你需要把 consumed[1]的值改成90,这样coordinatorLayout就能知道只处理剩下的10px的滚动

onNestedScroll

/**
* 进行嵌套滚动时被调用
* 一般可以用来处理向下滑动时的逻辑
* @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);
}


上一个方法结束后,NSC 处理剩下的距离。比如上面还剩 10px,这里 NSC 滚动 2px 后发现已经到头了,于是 NSC 结束其滚动,调用该方法,并将 NSC 处理剩下的像素数作为参数(dxUnconsumed、dyUnconsumed)传过来,这里传过来的就是 8px。参数中还会有 NSC 处理过的像素数(dxConsumed、dyConsumed)。这个方法主要处理一些越界后的滚动

onStopNestedScroll

/**
*  嵌套滚动结束时被调用,这是一个清除滚动状态等的好时机。
* @param coordinatorLayout
* @param child
* @param target
*/
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
}


一切滚动停止后调用,如果不会发生惯性滚动,fling 相关方法不会调用,直接执行到这里。这里我们做一些清理工作,当然有时也要处理中间态问题

onNestedScrollAccepted

/**
* 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);
}


当 NSP 接受要处理本次滑动后,这个回调被调用,我们可以做一些准备工作,比如让之前的滑动动画结束

onNestedPreFling

/**
* 用户松开手指并且会发生惯性动作之前调用,参数提供了速度信息,可以根据这些速度信息
* 决定最终状态,比如滚动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);
}


用户松开手指并且会发生惯性滚动之前调用。参数提供了速度信息,我们这里可以根据速度,决定最终的状态是展开还是折叠,并且启动滑动动画。通过返回值我们可以通知 NSC 是否自己还要进行滑动滚动,一般情况如果面板处于中间态,我们就不让 NSC 接着滚了,因为我们还要用动画把面板完全展开或者完全折叠

参考:

http://www.jianshu.com/p/82d18b0d18f4

http://www.jianshu.com/p/7f50faa65622
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: