Android嵌套滚动的传统方法与思路
前言
Android 的嵌套滚动,实现比较方便
- 横着滚动,ViewPager2
- 竖着滚动,NestedScrollingParent
顶上,有一个头部视图 header,
中间,有一个菜单视图 menu,
下面的是,内容视图, 一个 ViewPager2,包含几个 Tab,
Tab 里面是列表 RecyclerView
本文,主要参考 hongyangAndroid/Android-StickyNavLayout
Java 实现
基于 LinearLayout ,添加 NestedScrollingParent
子 View 开始滚动时,请求父 View 是否开始接受嵌套滚动,
SCROLL_AXIS_HORIZONTAL = 1
SCROLL_AXIS_VERTICAL = 2
水平方向,返回 false, 表示不接受;
( 不接受,则水平滚动,对竖直方向的滚动,没有干涉 )
竖直方向,返回 true, 表示接受。
public class StickyNavLayout extends LinearLayout implements NestedScrollingParent { @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { if (nestedScrollAxes == 1){ return false; } else{ return true; } } }
返回嵌套滚动的方向
@Override public int getNestedScrollAxes() { return ViewCompat.SCROLL_AXIS_VERTICAL; }
子视图纵向滚动,带动父视图的纵向滚动
目标视图执行嵌套滚动前的回调,
dx,dy 为产生的滚动距离,( 目标视图,就是拖动的子视图, RecyclerView )
( 纵向滚动, dx 为 0 )
consumed 为父 View 消耗的滚动距离
@Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { // 根据子视图的滚动偏移 dy // 和父视图的滚动偏移 getScrollY() // 确定子视图纵向滚动,带动父视图的纵向滚动 boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight; boolean showTop = dy < 0 && getScrollY() >= 0 && !target.canScrollVertically(-1); if (hiddenTop || showTop) { scrollBy(0, dy); consumed[1] = dy; } }
效果增强, 动画
往上轻滚,就把 header 遮盖;
往下轻滚,就显示 header
private int TOP_CHILD_FLING_THRESHOLD = 3; @Override public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { //如果是 recyclerView 根据判断第一个元素是哪个位置,可以判断是否消耗 //这里判断,如果第一个元素的位置是大于 TOP_CHILD_FLING_THRESHOLD 的 //认为已经被消耗,在 animateScroll 里不会对 velocityY<0 时做处理 if (target instanceof RecyclerView && velocityY < 0) { // 对子视图为 RecyclerView, 专门处理 final RecyclerView recyclerView = (RecyclerView) target; final View firstChild = recyclerView.getChildAt(0); final int childAdapterPosition = recyclerView.getChildAdapterPosition(firstChild); consumed = childAdapterPosition > TOP_CHILD_FLING_THRESHOLD; } // 动效 animateScroll(velocityY, 700, consumed); return true; }
动画滚动
使用 ValueAnimator ,做滚动动画
private ValueAnimator mOffsetAnimator; private void animateScroll(float velocityY, final int duration,boolean consumed) { final int currentOffset = getScrollY(); final int topHeight = mTop.getHeight(); if (mOffsetAnimator == null) { // 之前不存在动画,就新建 mOffsetAnimator = new ValueAnimator(); mOffsetAnimator.setInterpolator(mInterpolator); mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { if (animation.getAnimatedValue() instanceof Integer) { scrollTo(0, (Integer) animation.getAnimatedValue()); } } }); } else { // 之前存在动画,就取消 mOffsetAnimator.cancel(); } mOffsetAnimator.setDuration(Math.min(duration, 600)); if (velocityY >= 0) { // 向上滚动 // 隐藏 header mOffsetAnimator.setIntValues(currentOffset, topHeight); mOffsetAnimator.start(); }else if( !consumed ){ // 向下滚动 // 显示 header // 如果子 View 没有消耗 down 事件 那么就让自身滑到 0 位置 mOffsetAnimator.setIntValues(currentOffset, 0); mOffsetAnimator.start(); } }
总结
到此这篇关于Android嵌套滚动的传统方法与思路的文章就介绍到这了,更多相关Android嵌套滚动内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
赞 (0)