之前听同事提起过 NestedScrollingView,但是一直没有时间去了解,最近一段时间比较空,才开始去了解。先点开,看 NestedScrollingView 源码:
public class NestedScrollView extends FrameLayout implements NestedScrollingParent, NestedScrollingChild, ScrollingView

可以发现实现了两个没有见过的接口:NestedScrollingParent, NestedScrollingChild,也是这篇文章的主角。 Android 就是通过这两个接口, 来实现 子 View 与父View 之间的嵌套滑动。这样的嵌套滑动机制是在 Android 发布 Lollipop 之后提供的。不过同样在Support v7 中同样支持了。同时 RecycleView  以及 Android 5.0 以上的系统原声 View 大部分都已经支持嵌套滑动了 。

NestedScrolling 机制能够让父 View 和子 View 在滚动式进行配合,而要实现这样的交互机制,首先父 view 要实现 NestedScrollingParent 接口,而子 View 需要实现 NestedScrollingChild 接口,在这套机制中子 View是发起者,父 view 是接受回调并做出响应的。

四个主要接口

当我们自己再去写的时候,会碰到以下这四个类(接口):
// 主要接口
NestedScrollingChild
NestedScrollingParent
// 帮助类
NestedScrollingChildHelper
NestedScrollingParentHelper

正如前面所说的,很多 view 现在都实现了这两个接口:

  • NestedScrollView 已经实现了 NestedScrollingChild 和 NestedScrollingParent 两个接口

  • RecycleView 已经实现了 NestedScrollingChild

  • CoordinatorLayout 实现了 NestedScrollingParent

那实现这两个接口都需要做哪些工作呢?

要实现 NestedScrollingChild 接口,那么需要重写下面几个方法:

// 开始、停止嵌套滚动
public boolean startNestedScroll(int axes);
public void stopNestedScroll();
// 触摸滚动相关
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
// 惯性滚动相关 public boolean dispatchNestedPreFling(float velocityX, float velocityY);
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
public boolean dispatchNestedPreFling(float velocityX, float velocityY);
// 设置是否能够滑动
public void setNestedScrollingEnabled(boolean enabled);
public boolean isNestedScrollingEnabled(); 

要实现 NestedScrollingParent 接口,那么需要重写下面几个方法:

// 当开启、停止嵌套滚动时被调用
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
public void onStopNestedScroll(View target);
// 当触摸嵌套滚动时被调用
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed);
// 当惯性嵌套滚动时被调用
public boolean onNestedPreFling(View target, float velocityX, float velocityY);
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);

现在我们大概有些了解了,但是这些方法之间的逻辑关系是怎样的呢?

调用流程

整个对应流程是这样

子view 父view
startNestedScroll onStartNestedScroll、onNestedScrollAccepted
dispatchNestedPreScroll onNestedPreScroll
dispatchNestedScroll onNestedScroll
stopNestedScroll onStopNestedScroll
 
 
 
具体说明如下:
  • 在子 view 需要滑动的时候例如 ACTION_DOWN 的时候就要调用 startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL) 方法来告诉父 view 自己要开始滑动了(实质上是寻找能够配合 child 进行嵌套滚动的 parent)。

  • 父 view 会收到 onStartNestedScroll 回调从而决定是不是要配合子view做出响应。如果需要配合,此方法会返回 true。继而 onStartNestedScroll()回调会被调用。

  • 在滑动事件产生但是子 view 还没处理前可以调用 dispatchNestedPreScroll(0,dy,consumed,offsetInWindow) 这个方法把事件传给父 view 这样父 view 就能在onNestedPreScroll 方法里面收到子 view 的滑动信息,然后做出相应的处理把处理完后的结果通过 consumed 传给子 view。

  • dispatchNestedPreScroll()之后,child可以进行自己的滚动操作。

  • 如果父 view 需要在子 view 滑动后处理相关事件的话可以在子 view 的事件处理完成之后调用 dispatchNestedScroll 然后父 view 会在 onNestedScroll 收到回调。

  • 最后,滑动结束,调用 onStopNestedScroll() 表示本次处理结束。

过程清楚之后,我们再来看看一些方法的具体的含义,这样更好理解。

public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) ;

此方法的前两个参数用于告诉父 View 此次要滚动的距离;而第三第四个参数用于子 view 获取父 view 消费掉的距离和父 view 位置的偏移量。consumed 参数是一个 int 型的数组,长度为 2,第一个元素是父 view 消费的x方向的滚动距离;第二个元素是父 view 消费的 方向的滚动距离,如果这两个值不为0,则子view需要对滚动的量进行一些修正。正因为有了这个参数,使得我们处理滚动事件的时候,思路更加清晰,不会像以前一样被一堆的滚动参数搞混。

如果parent消费了一部分或全部距离,则此方法返回true。

public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);

在子 view 自己进行滚动之后调用此方法,询问父view是否还要进行余下 (unconsumed) 的滚动。

前四个参数为输入参数,用于告诉父view已经消费和尚未消费的距离,最后一个参数为输出参数,用于子view获取父view位置的偏移量。

返回值:  true if the event was dispatched, false if it could not be dispatched.

public void stopNestedScroll();

最后,stopNestedScroll()方法与startNestedScroll(int axes)对应,用于结束嵌套滚动流程;而惯性滚动相关的两个方法与触摸滚动相关的两个方法类似,这里不再赘述。

 

这里看三个例子,

1、https://github.com/543441727/MyNestedScrolling

2、这个是我改编的上面一个人写的代码,让它能够像下拉刷新那样工作:

https://github.com/huanshen/NestedScrollingParent-Child

3、https://github.com/qstumn/RefreshLayout

简单来说:

在 NestedScrollingChild 处理点击事件,然后发起滚动请求;

在 NestedScrollingParent 的 onNestedPreScroll 处理事件,是不是要自己消耗;

对于同时这两个接口的ViewGroup,需要在这两个里面都进行处理。如果子类是listview,则可以通过点击事件的拦截来进行控制,如果是recyclerView ,那么就不是点击事件的控制了。

参考文章:

NestedScrollingParent, NestedScrollingChild  详解

NestedScrolling的使用及滚动型的惯性滑动

Android -- NestedScrolling滑动机制

NestedScrollingParent, NestedScrollingChild 详解的更多相关文章

  1. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  2. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  3. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  4. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  5. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  6. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  7. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

  8. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

  9. Node.js npm 详解

    一.npm简介 安装npm请阅读我之前的文章Hello Node中npm安装那一部分,不过只介绍了linux平台,如果是其它平台,有前辈写了更加详细的介绍. npm的全称:Node Package M ...

随机推荐

  1. 局域网下的html注入及DNS劫持

    之前研究Dsploit的部分功能实现原理,现在了解的差不多了,简要记录下Dsploit的断网攻击,html注入,图片替换,cookie劫持的原理.本篇blog需要有一定的网络知识基础在看. 假设现有三 ...

  2. Java中的比较总结

    Java中的比较问题是一个很基础又很容易混淆的问题.今天就几个容易出错的点作一个比较详细的归纳与整理,希望对大家的学习与面试有帮助. 一.==与equals()的区别 首先,我们需要知道==与equa ...

  3. java日期详解

    [TOC] 一.简介 java中的日期处理一直是个问题,没有很好的方式去处理,所以才有第三方框架的地位比如joda. 文章主要对java日期处理的详解,用1.8可以不用joda. 1. 相关概念 首先 ...

  4. Android测试:Fundamentals of Testing

    原文地址:https://developer.android.com/training/testing/fundamentals.html 用户在不同的级别上与你的应用产生交互.从按下按钮到将信息下载 ...

  5. Java设计模式之职责链设计模式

    1.什么是-职责链设计模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求 ...

  6. CEF中JavaScript与C++交互

    在CEF里,JS和Native(C/C++)代码能够非常方便的交互,这里https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegr ...

  7. HDU 1013.Digital Roots【模拟或数论】【8月16】

    Digital Roots Problem Description The digital root of a positive integer is found by summing the dig ...

  8. R语言与数据分析之八:时间序列--霍尔特指数平滑法

    上篇我和小伙伴们分享了简单指数平滑法,简单指数平滑法仅仅能预測那些处于恒定水平和没有季节变动的时间序列,今天和大家分享非恒定水平即有增长或者减少趋势的.没有季节性可相加模型的时间序列预測算法---霍尔 ...

  9. vector删除元素与清除内存空洞

    问题:stl中的vector容器经常造成删除假象,这对于c++程序猿来说是极其讨厌的,<effective stl>大师已经将之列为第17条,使用交换技巧来修整过剩容量. 内存空洞这个名词 ...

  10. DocFX生成PDF文档

    使用DocFX生成PDF文档,将在线文档转换为PDF离线文档. 关于DocFX的简单介绍使用DocFX生成文档 使用docfx 命令 1.下载 https://github.com/dotnet/do ...