在项目中遇到了Behavior的使用,觉得很神奇,就记录一下。
Behavior大致介绍是:CoordinatorLayout是用来卸掉其字VIew们之间动作的一个父View,而Behavior就是用来给CoordinatorLayout的子View们实现交互。
- Behavior
在自定义Behaviors的时候,就要熟悉两个元素Childs
和deoendency
。其中要改变行为的那个View就是child,dependency是作为触发器影响child的那个View。 - 创建步骤:
- 继承
CoordinatorLayout.Behavior<T>
,其中T
就是child的类型,之后重写layoutDependsOn
和onDependentVIewChanged
。在每次UI变化的时候就会调用layoutDependsOn,鉴定完deoendency后一定要返回true。自动调用的layourDeoendsOn是用来控制child的行为。
- 继承
@Override
public boolean layoutDependsOn(
CoordinatorLayout parent,
CircleImageView, child,
View dependency) {
return dependency instanceof Toolbar;
}
layoutDependsOn返回true后就开始调用onDependentViewChanged
,在这个方法中我们利用dependency来实现动画,转换,动作。
public boolean onDependentViewChanged(
CoordinatorLayout parent,
CircleImageView avatar,
View dependency) {
modifyAvatarDependingDependencyState(avatar, dependency);
}
private void modifyAvatarDependingDependencyState(
CircleImageView avatar, View dependency) {
// avatar.setY(dependency.getY());
// avatar.setBlahBlat(dependency.blah / blah);
}
接下来就实现知乎的效果:
单独出场的底栏也可以利用上面一样的方法来设置隐藏或显示,我的底栏是和AppBarLayout一起出场,所以我就让底栏从属于AppBarLayout活动。
public class MyBottomBarBehavior extends CoordinatorLayout.Behavior<View> {
public MyBottomBarBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
//确定所提供的子视图是否有另一个特定的同级视图作为布局从属。
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
//这个方法是说明这个子控件是依赖AppBarLayout的
return dependency instanceof AppBarLayout;
}
//用于响应从属布局的变化
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float translationY = Math.abs(dependency.getTop());//获取更随布局的顶部位置
child.setTranslationY(translationY);
return true;
}
}
- FloatingActionButton和底栏上配置的是我们接下来要自定义的Behavior
ublic class ScaleDownShowBehavior extends FloatingActionButton.Behavior {
/**
* 退出动画是否正在执行。
*/
private boolean isAnimatingOut = false;
private OnStateChangedListener mOnStateChangedListener;
public ScaleDownShowBehavior(Context context, AttributeSet attrs) {
super();
}
//在嵌套滑动开始前回调
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
//在嵌套滑动进行时,对象消费滚动距离前回调
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
//dyConsumed 大于0是向上滚动 小于0是向下滚动
if (((dyConsumed > 0 && dyUnconsumed == 0) || (dyConsumed == 0 && dyUnconsumed > 0)) && child.getVisibility() != View.VISIBLE) {// 显示
AnimatorUtil.scaleShow(child, null);
if (mOnStateChangedListener != null) {
mOnStateChangedListener.onChanged(false);
}
} else if (((dyConsumed < 0 && dyUnconsumed == 0) || (dyConsumed == 0 && dyUnconsumed < 0)) && child.getVisibility() != View.GONE && !isAnimatingOut) {
AnimatorUtil.scaleHide(child, viewPropertyAnimatorListener);
if (mOnStateChangedListener != null) {
mOnStateChangedListener.onChanged(true);
}
}
}
public void setOnStateChangedListener(OnStateChangedListener mOnStateChangedListener) {
this.mOnStateChangedListener = mOnStateChangedListener;
}
// 外部监听显示和隐藏。
public interface OnStateChangedListener {
void onChanged(boolean isShow);
}
public static <V extends View> ScaleDownShowBehavior from(V view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)) {
throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
}
CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params).getBehavior();
if (!(behavior instanceof ScaleDownShowBehavior)) {
throw new IllegalArgumentException("The view is not associated with ScaleDownShowBehavior");
}
return (ScaleDownShowBehavior) behavior;
}
private ViewPropertyAnimatorListener viewPropertyAnimatorListener = new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
isAnimatingOut = true;
}
@Override
public void onAnimationEnd(View view) {
isAnimatingOut = false;
view.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(View arg0) {
isAnimatingOut = false;
}
};
}
我们通过重写Behavior中关于嵌套滑动的两个回调完成了FloatingActionButton的隐藏和显示判断及操作。
参考网站:
1. 一个神奇的控件——Android CoordinatorLayout与Behavior使用指南
字数1108 阅读16186 评论41 喜欢227
2. 关于CoordinatorLayout与Behavior的一点分析
3. CoordinatorLayout 自定义Behavior并不难,由简到难手把手带你撸三款
4. android-[译]掌握CoordinatorLayout
5. 深入理解CoordinatorLayout.Behavior