Android嵌套滚动 Android嵌套滚动的传统方法与思路
东方教主 人气:0想了解Android嵌套滚动的传统方法与思路的相关内容吗,东方教主在本文为您仔细讲解Android嵌套滚动的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:android嵌套滚动,android开发,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(); } }
总结
加载全部内容