NestedScrollView与RecyclerView的嵌套使用 您所在的位置:网站首页 recyclerview横向滑动 NestedScrollView与RecyclerView的嵌套使用

NestedScrollView与RecyclerView的嵌套使用

2023-03-16 06:50| 来源: 网络整理| 查看: 265

ScreenRecord.gif 使用NestedScrollView优化嵌套RecyclerView

在开发中经常会遇到ScrollView嵌套RecyclerView的情况。 例如界面需要一个banner,一段介绍文字,还有个列表,banner要可以划出界面,介绍文字要滑动后固定在顶部

开发中有两种解决办法 : 1 整个页面使用RecyclerView,根据类型返回不同的ViewHolder,这也是我正常用的,这次学习下下面的方法 2 使用NestedScrollView 包裹RecyclerView.(这个可以直接使用,但是需要点小优化)

NestedScrollView

NestedScrollView 和scrollView一样的使用,直接包裹一个子控件就可以了,它实现了 NestedScrollingParent, NestedScrollingChild2这两个方法

实现NestedScrollingParent的意思就是 我是个嵌套滑动的父控件,我可以和子滑动控件一起处理滑动事件。NestedScrollView嵌套RecyclerView主要就是关注这个 实现NestedScrollingChild2的意思是 我是个嵌套滑动的子控件,我滑动的时候要告诉父嵌套滑动控件,滑动之前要问问他是否消耗滑动事件。消耗掉的话 我就不滑动了,这个是NestedScrollView作为子控件的时候关注的

而RecyclerView则是实现了NestedScrollingChild2 他只能作为滑动的嵌套子控件

在滑动前通知父控件,如果父控件消耗了滑动距离 则返回的consumed里面的值就不为0 abstract boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow, int type) Dispatch one step of a nested scroll in progress before this view consumes any portion of it. 滑动的时候告诉父控件,因为NestedScrollView和RecylerView里面已经处理好了,我们这次没用到 abstract boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow, int type) Dispatch one step of a nested scroll in progress. 是否有嵌套的滑动父控件 abstract boolean hasNestedScrollingParent(int type) Returns true if this view has a nested scrolling parent for the given input type. 告诉父控件开始滑动了,如果有父滑动控件,并且父滑动控件想要和子控件一起处理滑动的话,就会返回True abstract boolean startNestedScroll(int axes, int type) Begin a nestable scroll operation along the given axes, for the given input type. 停止嵌套滑动了 abstract void stopNestedScroll(int type) Stop a nested scroll in progress for the given input type. 复制代码

RecyclerView中有个成员变量

private NestedScrollingChildHelper mScrollingChildHelper; private NestedScrollingChildHelper getScrollingChildHelper() { if (mScrollingChildHelper == null) { mScrollingChildHelper = new NestedScrollingChildHelper(this); } return mScrollingChildHelper; } 复制代码

对应的NestedScrollView里面

private final NestedScrollingParentHelper mParentHelper; mParentHelper = new NestedScrollingParentHelper(this); 复制代码

NestedScrollingChildHelper和NestedScrollingParentHelper都是系统提供的帮助类,已经封装好滑动调用逻辑,我们的关注点其实是在接口的回调上面。例如NestedScrollView的接口NestedScrollingParent

abstract int getNestedScrollAxes() Return the current axes of nested scrolling for this NestedScrollingParent. abstract boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) Request a fling from a nested scroll. abstract boolean onNestedPreFling(View target, float velocityX, float velocityY) React to a nested fling before the target view consumes it. abstract void onNestedPreScroll(View target, int dx, int dy, int[] consumed) React to a nested scroll in progress before the target view consumes a portion of the scroll. abstract void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) React to a nested scroll in progress. abstract void onNestedScrollAccepted(View child, View target, int axes) React to the successful claiming of a nested scroll operation. abstract boolean onStartNestedScroll(View child, View target, int axes) React to a descendant view initiating a nestable scroll operation, claiming the nested scroll operation if appropriate. abstract void onStopNestedScroll(View target) React to a nested scroll operation ending. 复制代码

要实现效果的话: 1 当banner在顶部的时候 不管手指在哪滑动,都是NestedScrollView滑动 2 当banner已经划过顶部的时候,手指在RecyclerView中滑动的时候,是RecyclerView滑动

我们demo中这个阀值就是banner的高度,上面说的是相应切换,其实并没有,只是父控件有没有消耗掉滑动距离的问题。子控件滑动前都会告诉父控件,父控件消耗掉了话,子控件就不做响应 在RecyclerView的OnTouchEvent中

if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) { dx -= mScrollConsumed[0]; dy -= mScrollConsumed[1]; vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]); // Updated the nested offsets mNestedOffsets[0] += mScrollOffset[0]; mNestedOffsets[1] += mScrollOffset[1]; } 复制代码

dx和dy都要减去父控件消耗的距离,如果父控件把滑动距离全消耗掉了的话,那么RecyclerView就不会滑动了 我继承了NestedScrollView并重写了OnNestedPreScroll,逻辑是如果NestedScrollView的滑动距离没有超过阀值,NestedScrollView就消耗掉全部的距离,超过了就全交给子控件自己处理。 只要做这一件事就可以了 就是这么简单 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { super.onNestedPreScroll(target, dx, dy, consumed);

if (mScrollY < mParentScrollHeight) { consumed[0] = dx; consumed[1] = dy; scrollBy(0, dy); } Log.d(TAG,"dx " + dx + " dy "+ dy + " " + consumed[0] + " " + consumed[1] + " scrollY " + mScrollY); } 复制代码

还有个问题是NestedScrollView嵌套RecyclerView的话,滑动问题解决了,但是RecyclerView会绘制出所有的item,如果列表很大的话就完蛋了,所以我们需要固定RecyclerView的高度。 高度就是rootView的高度-栏目类型view的高度

rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this); int rvNewHeight = rootView.getHeight() - topView2.getHeight(); rv.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,rvNewHeight)); } 复制代码

另外还遇到个问题,NestedScrollView嵌套RecyclerView时,固定高度后打开界面时会自动滑到底部。只需要在NestedScrollView的子view中加入 android:descendantFocusability="blocksDescendants"

最后 demo地址



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有