前面我们已经了解了 Google 的 nestedScrolling 嵌套滚动和 Behavior 这个接口,那么我们就开始实战了,从最简单的开始
开始第一个例子,仿知乎页面。知乎大家肯定用过,知乎首页在我们手指上滑查看跟过内容时,上面的标题栏和下面的导航是收起隐藏的,相反的当我们手指下滑,查看之前的内容时,上面的标题栏和下面的导航会再次显示出来。
知乎:
我实现的效果:
分析逻辑
其实这个很简单,我们需要监听控件垂直方向的滚动,手指上滑隐藏,手指下滑显示。这里我们不用去绑定依赖其他的 view,我们直接消费顶层父控件 CoordinatorLayout 传递给我们的滚动事件就行。并且我们在判断到手指移动后只要直接启动隐藏和显示的动画就行,不用根据手机移动的具体值进行偏移,所以这个效果还是很简单的,在代码上我们就是监听以下这2个方法即可:
- onStartNestedScroll
根据方向判断,我们要不要参与这次嵌套滚动,计算控件需要位移的数据 - onNestedPreScroll
根据方向,执行动画
标题栏我们不用自己写,使用系统的 AppBarLayout 包裹一个 Toolbar 就行,底部的导航栏自己写一个 behavior,至于 fab 也是和底部的导航栏一样的,这里我就没加。
xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:title="仿知乎页面"></android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:layout_behavior="@string/buttom_behavior">
<TextView
android:layout_width="0dp"
android:layout_height="40dp"
android:background="@color/colorAccent"
android:gravity="center"
android:text="底部导航条"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>
</android.support.design.widget.CoordinatorLayout>
behavior:
public class ButtomNavigationBehavior extends CoordinatorLayout.Behavior {
int offsetY = 0;
AnimatorUtils animatorUtils;
// 这个构造方法必须重写,CoordinatorLayout 会使用这个构造方法 new 一个直接子 view 的 Behavior 对象
public ButtomNavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
animatorUtils = new AnimatorUtils();
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
// 我们只需要在产生垂直方向的滚动时进行乔套滚动
if (nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL) {
// 获取控件左上角到父控件底部的具体
offsetY = coordinatorLayout.getMeasuredHeight() - child.getTop();
return true;
}
return false;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
// dy>0 ,表示上滑,查看更多内容,上下导航栏要隐藏
if (dy > 0) {
animatorUtils.startHindAnimator(child, offsetY);
return;
}
// dy<0 ,表示下滑,查看前面内容,上下导航栏要显示
if (dy < 0) {
animatorUtils.startShowAnimator(child, offsetY);
return;
}
}
public class AnimatorUtils {
public boolean isAnimator = false;
public boolean isShow = true;
public boolean isHind = false;
public int offsetY = 0;
public void startHindAnimator(View view, int offsetY) {
if (isAnimator || isHind) {
return;
}
ViewPropertyAnimatorCompat hindPropertyAnimatorCompat = ViewCompat.animate(view).translationY(offsetY).setListener(new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
isAnimator = true;
}
@Override
public void onAnimationEnd(View view) {
isAnimator = false;
isHind = true;
isShow = false;
}
@Override
public void onAnimationCancel(View view) {
isAnimator = false;
}
}).setDuration(200);
hindPropertyAnimatorCompat.start();
}
public void startShowAnimator(View view, int offsetY) {
if (isAnimator || isShow) {
return;
}
ViewPropertyAnimatorCompat showPropertyAnimatorCompat = ViewCompat.animate(view).translationY(0).setListener(new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
isAnimator = true;
}
@Override
public void onAnimationEnd(View view) {
isAnimator = false;
isHind = false;
isShow = true;
}
@Override
public void onAnimationCancel(View view) {
isAnimator = false;
}
}).setDuration(200);
showPropertyAnimatorCompat.start();
}
}
}
最后
这个效果说实话真是最简单的了,监听方向,执行动画就行,动画迁移量都是固定的,不用岁手指变化。这个效果就是让大家先熟悉下自定义 behavior 的思路
demo 地址: github