简介
前段时间由于产品的”神奇”(qi pa)需求,需要能下拉刷新放大,上划折叠,而且下半部分还是一个Tab+Viewpager布局。
类似新浪微博的个人中心,但他的也不能下拉放大背景图,百度良久,发现有个别案例相似,比如appbarlayout-spring-behavior,作者的源码似乎是深入阅读了Framework
层AppBarLayout
的Behavior
了,但是我做demo时,发现在fragment中根本用不了(具体原因太久不清楚了),无奈我只能自己撸个轮子了,这才静下心来研究了这个Behavior
,写这篇文章旨在帮助各位少走弯路,少爬坑。
注:文章会分为两大部分,前部分讲自定义behavior的基础知识,后部分讲例子的实现,如果你已经非常熟悉Behavior
的基础知识,请直接跳过第一部分。
图片.avi
自定义Behavior的基础知识
废话少说,只讲关键
Behavior是Android新出的Design库里新增的布局概念。Behavior只有是CoordinatorLayout的直接子View才有意义,我们可以为任何View添加一个Behavior。说白了Behavior就是一系列回调,让你有机会以非侵入的方式为View添加动态的依赖布局,和处理父布局(CoordinatorLayout)滑动手势的机会。
Interaction behavior plugin for child views of CoordinatorLayout.
A Behavior implements one or more interactions that a user can take on a child view. These interactions may include drags, swipes, flings, or any other gestures.
1.创建一个类继承Behavior,同时指定其泛型,如果你需要设置其为某个特定的View类,可以在继承的时候做的工作,类似这样
public class CircleImageInUsercBehavior extends CoordinatorLayout.Behavior<CircleImageView> { public CircleImageInUsercBehavior(Context context, AttributeSet attrs) { super(context, attrs); } }
如何设置上去呢?两种方式
① 可以在xml中直接指定你的Behavior,就像这样
app:layout_behavior=“你的Behavior的全路径”
②在代码里动态注册一个,就像AppBarLayout那样
@DefaultBehavior(AppBarLayout.Behavior.class)public class AppBarLayout extends LinearLayout {}
2.有哪些有用的方法可以重写呢?
@Override //当依赖的View发生变化时,主要用于跟随其做运动,如果只是简单的动画,就可以在这里实现了 public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { //这里实现自己的运动的方法, //child代表的是当前使用这个Behavior的View啦 //dependency就是我们所依赖的对象 return true; } @Override //确定你是否依赖于这个View public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return dependency.getId() == targetId; }
这里需要说明的一点是,所谓的嵌套滚动是包含了滚动(scroll)和快速的划动(fling:vt.(尤指生气地)猛扔;急派;放肆地投入;扔在一边,抛弃vt.& vi.猛扑;猛冲;急伸)
@Override//开始嵌套滚动public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) { return true;//这里返回true,才会接受到后续滑动事件。}@Override//嵌套滚动的过程中public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {//进行滑动事件处理}@Override//快速的划动中public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) { return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); }@Override//即将开始嵌套滚动,每次滑动前,Child 先询问 Parent 是否需要滑动,即dispatchNestedPreScroll(),这就回调到 Parent 的onNestedPreScroll(),Parent 可以在这个回调中“劫持”掉 Child 的滑动,也就是先于 Child 滑动。public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) { super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); }@Override//即将开始快速划动,这里可以做一些对动画的缓冲处理,也就是我们如何去应对用户快速的操作public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) { return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); }
大致需要用到的方法都在这里了,本篇不做更深入的探讨
需要更详细的介绍,可以看看这篇文章
http://www.apkbus.com/blog-873055-77902.html
例子的实现
注意:以下内容可能引起您的轻度不适(xing fen),请慎重阅读
例子中呢,用到了两个Behavior:
1. 用户头像的放大以及缩小的Behavior(CircleImageInUsercBehavior),按照上面的方法,我们可以很明白的知道实现步骤了
继承
public class CircleImageInUsercBehavior extends CoordinatorLayout.Behavior<CircleImageView>
重写onDependentViewChanged,
//当dependency变化的时候调用 @Override public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) { //初始化一些基础参数 init(parent, child, dependency); //计算比例 ... //设置头像的大小 ViewCompat.setScaleX(child, percent); ViewCompat.setScaleY(child, percent); return false; }
啊?这样就搞定了?是的!就是这么easy!!
那我有一个问题了,是不是说每一个view想要做跟随动画,都得创建一个相应的Behavior呢?答案很明显是NO~!
看完下一个例子你就会明白了
2.另外一个Behavior(AppBarLayoutOverScrollViewBehavior)用途主要有以下3点:
控制背景图的放大以及回弹
中间middle部分跟随背景图的放大缩小做相应的移动
Toolbar的背景Alpha的改变
第一步:初始化参数,通过tag查找每一个View,这里需要注意,我们需要在布局文件中,每个相应的View都需要声明相同的tag 如 android:tag="你的tag"
,当然,也可以用原始的findViewById,这里只是希望id改动时,我们的Behavior可以不受到影响
@Override public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, int layoutDirection) { ... if (mToolBar == null) { mToolBar = (Toolbar) parent.findViewWithTag(TAG_TOOLBAR); } ... abl.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { ...//实现Toolbar的背景变化 }); ... }
第二步:开始scale动画(下拉上划滑动过程中)
@Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) { if (!isRecovering) {//未在回弹动画中,开始我们的变化动画 if (...) { scale(child, target, dy); return; } } super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); @Override public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) { if (velocityY > 100) {//当y速度>100,就秒弹回 isAnimate = false; } return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); } }
第三步:松手的回弹
@Override public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target) { recovery(abl);//回弹,这个方法详细请看源码 super.onStopNestedScroll(coordinatorLayout, abl, target); }
ok,步骤就是这样,是不是很easy呢?
附:AppBarLayout的跟随动画,不仅仅是上面的一种方式
我们也可以在逻辑代码中通过原生的Listener来实现
mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { @Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { //计算进度百分比 float percent = Float.valueOf(Math.abs(verticalOffset)) / Float.valueOf(appBarLayout.getTotalScrollRange()); ...//根据百分比做你想做的 } });
http://www.apkbus.com/thread-600118-1-1.html
共同学习,写下你的评论
评论加载中...
作者其他优质文章