天气渐寒,然学习不可懈怠,记录一下使用NestedScrollView嵌套RecyclerView的两个问题,以后遇到可以来这里温故.

  应该说在MD中,RecyclerView代替了ListView,而NestedScrollView代替了ScrollView,他们两个都可以用来跟ToolBar交互,实现上拉下滑中ToolBar的变化。在NestedScrollView的名字中其实就可以看出他的作用了,Nested是嵌套的意思,而ToolBar基本需要嵌套使用.

  • 问题一,使用NestedScrollView嵌套RecyclerView时,滑动lRecyclerView列表会出现强烈的卡顿感.

体验极其不流畅,这不是我们希望的.于是,百度了一下轻松找到解决办法.

mRecyclerView.setNestedScrollingEnabled(false);

加上这句之后,整个世界都平静了,非常流畅啊!

这里面做了啥?

public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
...
}

点击RecyclerView源码,发现其实现了ScrollingView和NestedScrollingChild两个接口.

ScrollingView点进源码查看.

public interface ScrollingView {
/**
* <p>Compute the horizontal range that the horizontal scrollbar
* represents.</p>
*
* @return the total horizontal range represented by the horizontal
* scrollbar
*
* @see #computeHorizontalScrollExtent()
* @see #computeHorizontalScrollOffset()
* @see android.widget.ScrollBarDrawable
*/
int computeHorizontalScrollRange(); /**
* <p>Compute the horizontal offset of the horizontal scrollbar's thumb
* within the horizontal range. This value is used to compute the position
* of the thumb within the scrollbar's track.</p>
* @return the horizontal offset of the scrollbar's thumb
*
* @see #computeHorizontalScrollRange()
* @see #computeHorizontalScrollExtent()
* @see android.widget.ScrollBarDrawable
*/
int computeHorizontalScrollOffset(); /**
* <p>Compute the horizontal extent of the horizontal scrollbar's thumb
* within the horizontal range. This value is used to compute the length
* of the thumb within the scrollbar's track.</p>
*
* @return the horizontal extent of the scrollbar's thumb
*
* @see #computeHorizontalScrollRange()
* @see #computeHorizontalScrollOffset()
* @see android.widget.ScrollBarDrawable
*/
int computeHorizontalScrollExtent(); /**
* <p>Compute the vertical range that the vertical scrollbar represents.</p>
*
*
* @return the total vertical range represented by the vertical scrollbar
*
* <p>The default range is the drawing height of this view.</p>
*
* @see #computeVerticalScrollExtent()
* @see #computeVerticalScrollOffset()
* @see android.widget.ScrollBarDrawable
*/
int computeVerticalScrollRange(); /**
* <p>Compute the vertical offset of the vertical scrollbar's thumb
* within the horizontal range. This value is used to compute the position
* of the thumb within the scrollbar's track.</p>
*
* @return the vertical offset of the scrollbar's thumb
*
* @see #computeVerticalScrollRange()
* @see #computeVerticalScrollExtent()
* @see android.widget.ScrollBarDrawable
*/
int computeVerticalScrollOffset(); /**
* <p>Compute the vertical extent of the vertical scrollbar's thumb
* within the vertical range. This value is used to compute the length
* of the thumb within the scrollbar's track.</p>
*
*
* @return the vertical extent of the scrollbar's thumb
*
* @see #computeVerticalScrollRange()
* @see #computeVerticalScrollOffset()
* @see android.widget.ScrollBarDrawable
*/
int computeVerticalScrollExtent();
}

可以看出该接口主要关联横向和纵向滑动距离的计算.那NestedScrollingChild呢?这是个什么?

点进其源码,经过一顿翻译和分析,发现其定义的方法并不多:

public interface NestedScrollingChild {
/**
* 设置嵌套滑动是否能用
*
* @param enabled true to enable nested scrolling, false to disable
*/
public void setNestedScrollingEnabled(boolean enabled); /**
* 判断嵌套滑动是否可用
*
* @return true if nested scrolling is enabled
*/
public boolean isNestedScrollingEnabled(); /**
* 开始嵌套滑动
*
* @param axes 表示方向轴,有横向和竖向
*/
public boolean startNestedScroll(int axes); /**
* 停止嵌套滑动
*/
public void stopNestedScroll(); /**
* 判断是否有父View 支持嵌套滑动
* @return whether this view has a nested scrolling parent
*/
public boolean hasNestedScrollingParent(); /**
* 在子View的onInterceptTouchEvent或者onTouch中,调用该方法通知父View滑动的距离
*
* @param dx x轴上滑动的距离
* @param dy y轴上滑动的距离
* @param consumed 父view消费掉的scroll长度
* @param offsetInWindow 子View的窗体偏移量
* @return 支持的嵌套的父View 是否处理了 滑动事件
*/
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow); /**
* 子view处理scroll后调用
*
* @param dxConsumed x轴上被消费的距离(横向)
* @param dyConsumed y轴上被消费的距离(竖向)
* @param dxUnconsumed x轴上未被消费的距离
* @param dyUnconsumed y轴上未被消费的距离
* @param offsetInWindow 子View的窗体偏移量
* @return true if the event was dispatched, false if it could not be dispatched.
*/
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow); /**
* 滑行时调用
*
* @param velocityX x 轴上的滑动速率
* @param velocityY y 轴上的滑动速率
* @param consumed 是否被消费
* @return true if the nested scrolling parent consumed or otherwise reacted to the fling
*/
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed); /**
* 进行滑行前调用
*
* @param velocityX x 轴上的滑动速率
* @param velocityY y 轴上的滑动速率
* @return true if a nested scrolling parent consumed the fling
*/
public boolean dispatchNestedPreFling(float velocityX, float velocityY);
}

再回头看下RecyclerView中

public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {

public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
mClipToPadding = a.getBoolean(0, true);
a.recycle();
} else {
mClipToPadding = true;
}
setScrollContainer(true);
  //默认获取焦点
setFocusableInTouchMode(true); final ViewConfiguration vc = ViewConfiguration.get(context);
mTouchSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER); mItemAnimator.setListener(mItemAnimatorListener);
initAdapterManager();
initChildrenHelper();
// If not explicitly specified this view is important for accessibility.
if (ViewCompat.getImportantForAccessibility(this)
== ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
ViewCompat.setImportantForAccessibility(this,
ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
mAccessibilityManager = (AccessibilityManager) getContext()
.getSystemService(Context.ACCESSIBILITY_SERVICE);
setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
// Create the layoutManager if specified. boolean nestedScrollingEnabled = true; if (attrs != null) {
int defStyleRes = 0;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
defStyle, defStyleRes);
String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
int descendantFocusability = a.getInt(
R.styleable.RecyclerView_android_descendantFocusability, -1);
if (descendantFocusability == -1) {
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
}
a.recycle();
createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes); if (Build.VERSION.SDK_INT >= 21) {
a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
defStyle, defStyleRes);
nestedScrollingEnabled = a.getBoolean(0, true);
a.recycle();
}
} else {
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
}
  //默认支持嵌套滚动
// Re-set whether nested scrolling is enabled so that it is set on all API levels
setNestedScrollingEnabled(nestedScrollingEnabled);
}   @Override
public void setNestedScrollingEnabled(boolean enabled) {
getScrollingChildHelper().setNestedScrollingEnabled(enabled);
} @Override
public boolean isNestedScrollingEnabled() {
return getScrollingChildHelper().isNestedScrollingEnabled();
} @Override
public boolean startNestedScroll(int axes) {
return getScrollingChildHelper().startNestedScroll(axes);
} @Override
public void stopNestedScroll() {
getScrollingChildHelper().stopNestedScroll();
} @Override
public boolean hasNestedScrollingParent() {
return getScrollingChildHelper().hasNestedScrollingParent();
} @Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow) {
return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, offsetInWindow);
} @Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
} @Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
} @Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
}

这是全部都交给getScrollingChildHelper()这个方法的返回对象处理了啊,看看这个方法是怎么实现的。

private NestedScrollingChildHelper getScrollingChildHelper() {
if (mScrollingChildHelper == null) {
mScrollingChildHelper = new NestedScrollingChildHelper(this);
}
return mScrollingChildHelper;
}

这样的话.NestedScrollingChild 接口的方法都交给NestedScrollingChildHelper这个代理对象处理了.

那么到这个类的源码中定位到我们关注方法的实现;

public class NestedScrollingChildHelper {
   private final View mView;
private ViewParent mNestedScrollingParent;
private boolean mIsNestedScrollingEnabled;
private int[] mTempNestedScrollConsumed; /**
* Construct a new helper for a given view.
*/
public NestedScrollingChildHelper(View view) {
mView = view;
} /**
* Enable nested scrolling.
*
* <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
* method/{@link android.support.v4.view.NestedScrollingChild} interface method with the same
* signature to implement the standard policy.</p>
*
* @param enabled true to enable nested scrolling dispatch from this view, false otherwise
*/
public void setNestedScrollingEnabled(boolean enabled) {
if (mIsNestedScrollingEnabled) {
ViewCompat.stopNestedScroll(mView);
}
mIsNestedScrollingEnabled = enabled;
}
  /**
  * Check if nested scrolling is enabled for this view.
  *
  * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
  * method/{@link android.support.v4.view.NestedScrollingChild} interface method with the same
  * signature to implement the standard policy.</p>
  *
  * @return true if nested scrolling is enabled for this view
  */
  public boolean isNestedScrollingEnabled() {
return mIsNestedScrollingEnabled;
  }
.
.
.
}

可以看出RecyclerView默认是setNestedScrollingEnabled(true),是支持嵌套滚动的,也就是说当它嵌套在NestedScrollView中时,默认会随着NestedScrollView滚动而滚动,放弃了自己的滚动.

所以给我们的感觉就是滞留、卡顿.主动将该值置false可以有效解决该问题.

  • 问题二,使用NestedScrollView嵌套RecyclerView时,每次打开界面都是定位在RecyclerView在屏幕顶端,列表上面的布局都被顶上去了.

这个问题花了我不少时间去查原因,最终定位到是RecyclerView抢占了焦点,自动滚动导致的.

查看RecyclerView的源码发现,它会在构造方法中调用setFocusableInTouchMode(true),所以抢到焦点后一定会定位到第一行的位置突出RecyclerView的显示

解决方法就是NestScrollView节点添加

android:focusableInTouchMode="true"

然后在NestScrollView的子节点view添加:

android:descendantFocusability="blocksDescendants"

或者 直接mRecyclerVIew.setFocusableInTouchMode(false)

  • 总结

不明白的地方还是要多看源码 , 看源码有几个好处.第一点,有些问题只能从源码里找出究竟 , 一点一点剖析才能看出问题的根本, 弄透一个接口的作用 , 那么有类似需求的时候,可以自定义控件实现该接口;再一个,就是在看的过程中, 我们会熟悉源码的风格,命令的方式和逻辑的先后 .总之 , 看一个好轮子的源码 , 是为了写轮子做准备.

NestedScrollView嵌套RecyclerView的更多相关文章

  1. Android 解决NestedScrollView 嵌套 RecyclerView出现的卡顿,上拉刷新无效

    解决卡顿的方法最简单的就是设置RecyclerView的android:nestedScrollingEnabled="false",放弃自己的滑动,交给外部的NestedScro ...

  2. 使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种滑动冲突

    使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种冲突 如果你还在为处理滑动冲突而发愁,那么你需要静 ...

  3. Android 使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种滑动冲突

    如果你还在为处理滑动冲突而发愁,那么你需要静下心来看看这边文章,如果你能彻底理解这篇文章中使用的技术,那么,一切滑动冲突的问题解决起来就轻而易举了: 先扔一个最终实现的效果图 先分析下效果图中实现的功 ...

  4. Android NestedScrollView与RecyclerView嵌套,以及NestedScrollView不会滚动到屏幕顶部解决

    ①NestedScrollView与RecyclerView嵌套,导致滚动惯性消失 解决:mRecyclerView.setNestedScrollingEnabled(false); ②Nested ...

  5. scrollview嵌套下拉控件嵌套recyclerview(不动第三方原基础自定义)

    相信会碰到很多类似的需求,一个列表控件,然后控件上方的一个头部需要自定义,这样就不好有时候也不能加在列表控件的头部了,那必须得嵌套一层scrollview了,没毛病,那么一般的列表控件都是有上拉下拉的 ...

  6. 解决ScrollView嵌套RecyclerView出现item显示不全的问题

      问题:ScrollView嵌套RecyclerView时,RecyclerView的item显示不全 出现问题不要慌,耐心解决才是王道,哈哈.首先说下出现这个问题的情景吧,首先声明这个问题在23版 ...

  7. Android在开发中的使用技巧之解决ScrollView嵌套RecyclerView出现的系列问题

    根据已上线的app里总结出来的实用小技巧 相信大家都遇到过ScrollView嵌套RecyclerView或者RecyclerView嵌套RecyclerView来使用, 也会遇到一堆奇奇怪怪的问题, ...

  8. NestedScrollView和RecyclerView使用,并设置间距

    NestedScrollView和RecyclerView使用,并设置间距: 效果图如下: 1.NestedScrollView 和RecyclerView嵌套问题(类似ScrollView 和lis ...

  9. NestedScrollView嵌套ListView可行性总结

    由于公司项目遗留代码仍然使用PullToRefreshListView(后文简称PTRLV),且存在复用,更换RecyclerView成本太大,同时又想使用CoordinatorLayout来实现一些 ...

随机推荐

  1. 腾讯QQ的聊天记录中的图片记录造假

    前不久和朋友在群里聊天时,突然出现了一个BUG,就是一个群友发了A图片,但在我这边显示得却是B图片.当时就猜测,腾讯为了节省流量或者手机资源的原因,给每一张图片弄了个唯一ID,遇到相同ID的就直接从本 ...

  2. BZOJ 3437 小P的牧场(斜率优化DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3437 [题目大意] n个牧场排成一行,需要在某些牧场上面建立控制站, 每个牧场上只能建 ...

  3. [SRM478]RandomApple

    题意:有$k$种苹果和$n$个箱子,每个箱子中有一些苹果,先等概率选取$n$个箱子组成集合的非空子集,再从选出的苹果中随机选一个,问每种苹果被选中的概率是多少 设箱子$i$有$cnt_{i,j}$个第 ...

  4. 【暴力】hdu6121 Build a tree

    给你n,K,让你构造出一颗n个结点的完全K叉树,求所有结点子树大小的异或和. 先把n号结点到根的路径提取出来单独计算.然后这条路径把每一层分成了左右两部分,每一层的左侧和其上一层的右侧的结点的子树大小 ...

  5. [转]Spring 中的p标签

    spring的bean配置文件中p:代表什么 <bean id="daoTemplate" abstract="true" lazy-init=" ...

  6. hibernate使用原生SQL查询

    以下是Demo测试Hibernate 原生SQL查询: import java.util.Iterator; import java.util.List; import java.util.Map; ...

  7. MYSQL复习笔记12-视图

    Date: 20140223Auth: Jin参考:http://blog.sina.com.cn/s/blog_436732df0100e768.html 一.介绍1.概念视图是从一个或几个基本表( ...

  8. mysql错误10060

    setenforce 0 service iptables stop mysql -u root -p Aaa111222333 GRANT ALL PRIVILEGES ON *.* TO 'roo ...

  9. nor flash 和nand flash 的区别

    ROM和RAM指的都是半导体存储器,ROM是Read Only Memory的缩写,RAM是Random Access Memory的缩写.ROM在系统停止供电的时候仍然可以保持数据,而RAM通常都是 ...

  10. 怎么查看和修改 MySQL 的最大连接数

    通常,mysql的最大连接数默认是100, 最大可以达到16384.1.查看最大连接数:show variables like '%max_connections%';2.修改最大连接数方法一:修改配 ...