之前听同事提起过 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. C# 通过反射初探ORM框架的实现原理

    背景: 以前学的Java进行开发,多用到Mybatis,Hiberante等ORM框架,最近需要上手一个C#的项目,由于不是特别难,也不想再去学习C#的ORM框架,所以就想着用反射简单的实现一下ORM ...

  2. layui使用心得

    表单控件样式没生效, 因为没引入form. layui.use('form', function(){ var form = layui.form; }); radio改变事件没生效, 需要手动fil ...

  3. MySQL-Select语句高级应用

    1.1 SELECT高级应用 1.1.1 前期准备工作 本次测试使用的是world数据库,由mysql官方提供下载地址: https://dev.mysql.com/doc/index-other.h ...

  4. POJ3621 Sightseeing Cows(最优比率环)

    题目链接:id=3621">http://poj.org/problem?id=3621 在一个有向图中选一个环,使得环上的点权和除以边权和最大.求这个比值. 经典的分数规划问题,我认 ...

  5. 泛泰A870(高通APQ8064t 600 cpu) Mokee4.4.2(Android4.4) 图赏

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3lob3N0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/d ...

  6. redis缓存的安装和配置

    ubantu16.04环境下安装 下载安装,依次执行命令; # 从官方网站下载安装包,注意,当前在哪个目录下执行命令,下载的包将在哪个目录下 $ wget http://download.redis. ...

  7. .NET Core RSA 签名和验签(密钥为 16 进制编码)

    使用 OpenSSL 生成公私钥对,命令: $ openssl genrsa -out rsa_1024_priv.pem $ openssl pkcs8 -topk8 -inform PEM -in ...

  8. MyBatis_通过resultMap解决不一致的问题

  9. 个人作业2:APP案例分析

    产品 产品名 网易云音乐 选择原因 除社交软件和浏览器以外,在手机里存在最久的也是使用次数最多的APP就是它了.不管换多少次手机和电脑,它始终在我的装机必备名单上. 调研与评测 第一次上手体验 第一次 ...

  10. 配置SQL Server on Linux(1)

    1. 背景 SQL Server一般是在安装过程中进行相关的配置,安装完成之后,再去修改有一些配置就比较麻烦,比如更改SQL Server实例级别的排序规则.但在Linux下,安装过程并没有很多可以配 ...