NestedScrolling机制(一)——概述

2023-11-17

http://blog.csdn.net/al4fun/article/details/53888990

如今,NestedScrolling机制(可以称为嵌套滚动或嵌套滑动)在各种app中的应用已经十分广泛了,下图是“饿了么”中的一个例子:

当向上滚动列表时,列表的父view(整个白色部分)会跟着一起向上滑动,当顶部的banner收缩到只剩标题栏时,则列表的父view保持固定,而列表继续滚动;当向下滚动列表时,则是相反的过程。

这种效果其实也可以通过CoordinatorLayout结合Behavior来实现,但阅读源码就会发现,CoordinatorLayout本身是一个NestedScrollingParent,此外,要实现这种效果还要求可滚动的列表是一个NestedScrollingChild,也就是说,通过CoordinatorLayout结合Behavior来实现这种效果,其内部原理也是NestedScrolling。

1 概述

NestedScrolling机制能够让父view和子view在滚动时进行配合,其基本流程如下:

  1. 当子view开始滚动之前,可以通知父view,让其先于自己进行滚动;
  2. 子view自己进行滚动
  3. 子view滚动之后,还可以通知父view继续滚动

要实现这样的交互,父View需要实现NestedScrollingParent接口,而子View需要实现NestedScrollingChild接口。

在这套交互机制中,child是动作的发起者,parent只是接受回调并作出响应

另外:父view和子view并不需要是直接的父子关系,即如果“parent1包含parent2,parent2包含child”,则parent1和child仍能通过nestedScrolling机制进行交互。

2 接口和类

//主要接口
NestedScrollingChild
NestedScrollingParent

//帮助类
NestedScrollingChildHelper
NestedScrollingParentHelper
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上面已经说过,要使用NestedScrolling机制,父View需要实现NestedScrollingParent接口,而子View需要实现NestedScrollingChild接口。

而NestedScrollingChildHelper和NestedScrollingParentHelper是两个帮助类,当我们在实现NestedScrollingChild和NestedScrollingParent接口时,使用这两个帮助类可以简化我们的工作。

以上接口和类都在support-v4包中提供。另外,一些较新的系统view都已经实现了NestedScrollingChild或NestedScrollingParent接口,也就是说他们直接支持NestedScrolling,例如:

  • NestedScrollView 已实现 NestedScrollingParent和NestedScrollingChild
  • RecyclerView 已实现 NestedScrollingChild
  • CoordinatorLayout 已实现 NestedScrollingParent
  • 等等

2.1 NestedScrollingChild接口

1 接口概述

//开始、停止嵌套滚动
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);
  1. public boolean startNestedScroll(int axes);

    开启嵌套滚动流程(实际上是进行了一些嵌套滚动前准备工作)。

    当找到了能够配合当前子view进行嵌套滚动的父view时,返回值为true(Returns:true if a cooperative parent was found and nested scrolling has been enabled for the current gesture)。

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

    在子view自己进行滚动之前调用此方法,询问父view是否要在子view之前进行滚动。

    此方法的前两个参数用于告诉父View此次要滚动的距离;而第三第四个参数用于子view获取父view消费掉的距离和父view位置的偏移量。

    第一第二个参数为输入参数,即常规的函数参数,调用函数的时候我们需要为其传递确切的值。而第三第四个参数为输出参数,调用函数时我们只需要传递容器(在这里就是两个数组),在调用结束后,我们就可以从容器中获取函数输出的值。

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

  3. 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.

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

2 接口实现

上面只是讲了接口中的主要方法和调用时机,那么这些方法具体该如何实现呢?这时候就要用到上面提到的帮助类了。具体操作很简单:首先实例化一个帮助类对象,然后在要实现的接口方法中调用帮助类对象中的同名方法即可——帮助类对象已经帮我们完成了一切。

@Override
public void setNestedScrollingEnabled(boolean enabled) {
   mScrollingChildHelper.setNestedScrollingEnabled(enabled);
}

@Override
public boolean isNestedScrollingEnabled() {
    return mScrollingChildHelper.isNestedScrollingEnabled();
}

@Override
public boolean startNestedScroll(int axes) {
    return mScrollingChildHelper.startNestedScroll(axes);
}

@Override
public void stopNestedScroll() {
    mScrollingChildHelper.stopNestedScroll();
}

@Override
public boolean hasNestedScrollingParent() {
    return mScrollingChildHelper.hasNestedScrollingParent();
}

@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
    return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
}

@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
    return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}

@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
    return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}

@Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
    return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

2.2 NestedScrollingParent接口

1 接口概述

//当开启、停止嵌套滚动时被调用
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);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

从命名可以看出,这几个都是回调方法。当调用NestedScrollingChild中的方法时,NestedScrollingParent中与之相对应的方法就会被回调。方法之间的具体对应关系如下:

子(发起者) 父(被回调)
startNestedScroll onStartNestedScroll、onNestedScrollAccepted
dispatchNestedPreScroll onNestedPreScroll
dispatchNestedScroll onNestedScroll
stopNestedScroll onStopNestedScroll

1.public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);

参数 说明
target: 发起嵌套滚动的子View,此子view必须实现NestedScrollingChild接口。上面提到过,此子view并不需要是当前view的直接子view
child: 当前view的包含target的直接子view
nestedScrollAxes: 嵌套滚动的方向,可能是SCROLL_AXIS_HORIZONTAL 或 SCROLL_AXIS_VERTICAL 或 二者都有

当调用target的startNestedScroll(int axes)时,此方法就会被调用。在此方法中我们要做的就是根据target和nestedScrollAxes决定此view是否要与target配合进行嵌套滚动,并返回true(要与target配合进行嵌套滚动)或false(不与target配合进行嵌套滚动)。

2.onNestedPreScroll()、onNestedPreScroll()、onNestedPreFling()、onNestedFling()

这几个方法分别对应NestedScrollingChild中的dispatchNestedPreScroll()、dispatchNestedScroll()、dispatchNestedPreFling()和dispatchNestedFling()。

它们的参数也是基本对应的,以onNestedPreScroll()为例,参数dx、dy、consumed实际就是dispatchNestedPreScroll()中的dx、int dy、consumed。

2 接口实现

onNestedScrollAccepted、onStopNestedScroll的实现同样是调用帮助类中的同名方法即可:

@Override
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
    mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
}

@Override
public void onStopNestedScroll(View target) {
    mParentHelper.onStopNestedScroll(target);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

而另外几个方法的实现则由你的业务逻辑而定(详见下一节中的代码示例)。

3 流程

经过以上的介绍,我们可以大致将嵌套滚动的流程概括如下(以触摸滚动为例,惯性滚动(fling)的流程与此类似):

  1. 调用child的startNestedScroll()来发起嵌套滚动流程(实质是寻找能够配合child进行嵌套滚动的parent)。parent的onStartNestedScroll()会被回调,如果此方法返回true,则onNestedScrollAccepted()也会被回调。
  2. child每次滚动前,可以先询问parent是否要滚动,即调用dispatchNestedPreScroll(),这会回调到parent的onNestedPreScroll(),parent可以在这个回调中先于child滚动。
  3. disdispatchNestedPreScroll()之后,child可以进行自己的滚动操作。
  4. child滚动以后,可以调用dispatchNestedScroll(),会回调到parent的onNestedScroll(),在这里parent可以进行后于child的滚动。
  5. 滚动结束,调用stopNestedScroll()

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NestedScrolling机制(一)——概述 的相关文章

  • -lz -lrt -lm -lc都是什么库

    libz librt libm libc 压缩库 Z 实时库 real time 数学库 math 标准C库 C lib
  • get it [springmvc controller 单例说明以及多例切换]

    spring的bean作用域种类 1 singleton 单例模式 当spring创建applicationContext容器的时候 spring会欲初始化所有的该作用域实例 加上lazy init就可以避免预处理 2 prototype
  • Junit单元测试

    概念 JUnit是一个 Java 编程语言的单元测试工具 可以对部分代码的进行测试 Junit是用于Java的单元测试的框架 是别人写好的 特点 JUnit是一个开放源代码的测试工具 提供注解来识别测试方法 JUnit测试可以让你编写代码更
  • npm安装vue-cli,一直停留在deprecated request@2.88.2: request has been deprecated, see https://github.com/req

    安装vue cli出现的错误 原因 资源问题 没有配置淘宝镜像 解决 配置淘宝镜像 npm config set registry https registry npm taobao org 重新安装vue cli 即可成功 npm ins
  • 多线程中sleep、yield、join的用法及sleep与wait区别

    Object中的wait notify notifyAll 可以用于线程间的通信 核心原理为借助于监视器的入口集与等待集逻辑 通过这三个方法完成线程在指定锁 监视器 上的等待与唤醒 这三个方法是以锁 监视器 为中心的通信方法 除了它们之外
  • 分析java源代码/开源框架源码的思路?

    讨论下大家分析源代码的思路 遇到源代码是怎样去分析的 我的思路基本是这样的 1 弄清楚这个框架 是做什么用的 分解功能 2 分解功能出来后 针对每个功能画出类框架图 3 找到功能入口 然后分析每个方法 有个疑惑 在分析方法的过程中 方法链会
  • 解决数组塌陷的两种方式

    解决数组塌陷的两种方式 1 i 2 将数组倒着循环遍历 转载于 https www cnblogs com oklfx p 8495060 html
  • vuex是什么?

    vuex解释 vuex是一个专门为vue js应用程序开发的状态管理模式 通俗点说就是我们项目中需要共享的一些数据的管理容器 这里的状态就是数据 那么什么情况下才应该使用vuex呢 简单的说就是当你在构建一个中大型单页用的时候 需要在组件外
  • QCombox隐藏某一项

    有事想隐藏下拉选项的某一项 而又不改变索引 可以使用如下方法 QListView view qobject cast
  • 设计模式之六大原则

    设计模式之六大原则 转载 关于设计模式的六大设计原则的资料网上很多 但是很多地方解释地都太过于笼统化 我也找了很多资料来看 发现CSDN上有几篇关于设计模式的六大原则讲述的比较通俗易懂 因此转载过来 原作者博客链接 http blog cs
  • Parallels desktop 10 虚拟机支持 USB 3.0

    自Parallels Desktop 8 0 18305 起虚拟机可支持USB 3 0 以Parallels Desktop 10 for Mac为例 如何在虚拟机启用USB 3 0 为了在虚拟机中启用 USB 3 0 请首先在配置中启用功
  • 快速排序————非递归实现

    二 递归实现快速排序 2 1 为什么我们要去通过递归实现我们的快速排序呢 大家有可能会想是不是因为递归非常的占用空间 我们都知道我们的局部变量是保存在栈上的我们的函数参数也是在栈上开辟的 所以说递归是不是会占用我们非常多的栈空间 同时呢我们
  • 【小沐学NLP】Python实现聊天机器人(ChatterBot,代码示例)

    NLP开发系列相关文章编写如下 1 小沐学NLP Python实现词云图 2 小沐学NLP Python实现图片文字识别 3 小沐学NLP Python实现中文 英文分词 4 小沐学NLP Python实现聊天机器人 ELIZA 5 小沐学
  • 矩阵复习三-正交矩阵

    如果ATA I 则A为正交矩阵 A为正交矩阵 则有 A的列向量组为一组标准正交基 若A B都为n阶正交矩阵 则有 A 1或 A 1 A的列向量组为一组标准正交基 A 1 AT A 1 AT也是正交矩阵 AB也是正交矩阵 Rn空间的线性变换矩
  • 创建分区表及分区索引(本地)

    创建表空间 SQL gt create tablespace myTableSpace 1 datafile dat DBData oradata NACEC myTableSpace1 dbf size 100m Tablespace c
  • ControlNet精准控制AI绘画教程

    ControlNet精准控制AI绘画教程 AI绘画相信大家都已经不陌生了 虽然AI绘画出图很方便 但是要让其生成一副自己满意的图 还是需要费一番心思 有时候多次调整关键词就是生成不了自己想要的画面 这些一直以来都是AI绘画的痛点但就在最近
  • Vue前端框架入门,真好学,都给我学起来

    前言 今天要分享的知识是Vue前端框架 码字不易 点个赞 转载请说明 开发工具 HBuilderX Eclipse 目录 一 Vue是什么 二 库和框架的区别 三 MVVM介绍 四 cdn的下载及入门案例 定义边界和绑定边界 案例二 数据双
  • bigdecimal加减乘除运算方法

    BigDecimal是Java中的一个类 用于处理高精度的十进制数值计算 在进行浮点数计算时 由于浮点数的精度有限 可能会出现精度丢失的情况 而BigDecimal可以避免这种情况的发生 因此在需要高精度计算的场合 使用BigDecimal

随机推荐

  • CentOS7安装oracle19c教程

    参考 https zhuanlan zhihu com p 571737575 1创建组和用户 vi etc hosts root rhel cat etc redhat release Red Hat Enterprise Linux S
  • 使用designer写pyqt程序

    1 创建一个qt程序 复制其ui文件至python文件夹 ui界面如下 2 vscode 配置pyqt集成环境 右击ui文件进行编译 生成UI mainwindow py文件
  • LNK2019: 无法解析的外部符号 整理

    无法解析的外部符号是Windows下C 编译的常见链接错误 收集整理备忘 本文随遇到的问题长期更新 我目前遇到的错误可以分为3类 1 编译工具链修改了对应库函数的定义 2 编译参数导致定义和链接库不一致 3 库依赖冲突 4 未导入对应库 常
  • WiFi密码别问了,这神器帮你搞定一切!

    我们经常会遇见朋好去你家做客 第一句就是问你家WiFi 密码 如果密码负责不仅说的麻烦 还有可能暴露自己的密码 毕竟很多人密码都喜欢设置的一样 但是今天这个GitHub 工具WiFi Card完全就能解决这个问题 这个工具就是把 WiFi
  • C++小白课本练习4

    练习目录 ConsoleApplication1 h 头文件定义类 Student myDate Student 类 myDate 类 第二章课本测试3验证类功能的驱动程序 cpp 第二章课本测试4使用指针的方式驱动程序 cpp 第二章课本
  • mysql数据库中文乱码的问题

    今天下午 在Qt中往mysql数据库中插入数据时 中文显示乱码 如下图所示 开始以为是数据库字符编码的问题 1 使用set character set database utf8 在命令行上修改字符编码 但是重启mysql之后 字符编码并没
  • Centos7系统防火墙使用教程【详解】

    CentOS 7是一种常见的Linux操作系统 防火墙作为网络安全的第一道防线 对于服务器的安全至关重要 本文将介绍CentOS 7系统中防火墙的使用教程 包括如何开启 关闭 配置以及防火墙规则的添加和删除 一 查看防火墙状态 在开始操作之
  • 关于Android Service真正的完全详解,你需要知道的一切

    Service全部内容基本会在本篇涉及到 我们将围绕以下主要知识点进行分析 Service简单概述 Service在清单文件中的声明 Service启动服务实现方式及其详解 Service绑定服务的三种实现方式 关于启动服务与绑定服务间的转
  • Verilog--CDC跨时钟域处理(快时钟域到慢时钟域)

    Verilog CDC跨时钟域处理 快时钟域到慢时钟域 CDC问题 单比特信号的跨时钟域问题 从快时钟域到慢时钟域 从慢时钟域到快时钟域 多比特信号的跨时钟域问题 异步FIFO 握手协议 DMUX 格雷码 双D触发器 今天先写单比特信号从快
  • 开始前准备

    开始前准备 一 环境预览 二 安装Ubuntu 三 安装arm gcc工具链 四 Ubuntu构建LiteOS所需要的工具链 五 安装STM32CubeMX软件 六 串口调试助手下载 七 ST Link和USB转TTL串口调试工具 一 环境
  • Express_2 Express Generator

    本文为课程笔记 总体来说 express generator可以帮助我们快速搭建一个express环境 首先 要将它安装在全局环境里 npm install express generator g 然后 就可以使用express 项目名 创
  • 数组入门练习:螺旋遍历二维数组

    NC38 螺旋矩阵 给出一个 n n n 行 m m m 列的二维数组 按螺旋的顺序返回矩阵中的所有元素 比如 输入为 1 2 3 4 5 6 7 8 9 输出为 1 2 3 6 9 8 7 4 5 观察上图
  • 关于mmsegmentation的自定义数据集格式的一些坑(2)

    这次又有任务要跑mmseg 我本意是拒绝的 奈何老板喜欢 mm系列的东西 那一套流程 真心玩不转 玩着玩着心就累了 mm系列和detectron差不多 要找其代码流转 都是要先看配置文件 比较麻烦 需要一定的经验 然后前面两篇都写了我是怎么
  • Android SELinux

    Google参考链接 https source android com docs core architecture aidl aidl hals sepolicy A 通信框架SE文件修改 public attributes vendor
  • 【canal系】canal集群异常Could not find first log file name in binary log index file

    这里先说明下这边使用的canal版本号为1 1 5 在描述这个问题之前 首先需要简单对于canal架构有个基本的了解 canal工作原理 canal 模拟 MySQL slave 的交互协议 伪装自己为 MySQL slave 向 MySQ
  • 详解@Override注解

    目录 1 是什么 2 为什么用 3 举例说明 1 示例一 2 示例二 3 示例三 1 是什么 Override注解是伪代码 用于表示被标注的方法是一个重写方法 Override注解 只能用于标记方法 并且它只在编译期生效 不会保留在clas
  • QT中添加Q_OBJECT出现的问题

    Multiple Inheritance Requires QObject to Be First 多重继承QObject一定要放在前面 我在用class My Node public QGraphicsItem public QObjec
  • 产业互联网-构建智能+时代数字生态新图景

    在2019腾讯全球数字生态大会新闻发布会上 腾讯云联合腾讯研究院 共同发布了行业重磅报告 产业互联网 构建智能 时代数字生态新图景 报告首次阐述了产业互联网的战略框架和实践方法论 报告指出 产业互联网的实现 需要跨界共建数字生态共同体 形成
  • linux安装telnet工具下载,Linux下安装telnet的方法

    一 安装telnet 1 检测telnet server的rpm包是否安装 root localhost rpm qa telnet server 若无输入内容 则表示没有安装 出于安全考虑telnet server rpm是默认没有安装的
  • NestedScrolling机制(一)——概述

    http blog csdn net al4fun article details 53888990 如今 NestedScrolling机制 可以称为嵌套滚动或嵌套滑动 在各种app中的应用已经十分广泛了 下图是 饿了么 中的一个例子 当