嵌套滚动介绍

我们知道 NestedScrolling(Parent/Child) 这对接口是用来实现嵌套滚动的,一般实现这对接口的 Parent 和 Child 没有直接嵌套,否则直接用 onInterceptTouchEvent() 和 onTouchEvent() 这对方法实现就可以了。能够越级嵌套滚动正是它的厉害之处。

嵌套滚动的接口有两对:NestedScrolling(Parent/Child) 和 NestedScrolling(Parent2/Child2) 后者相比前者对 fling 的处理更加细致。相比第一代 Child 简单地将 fling 抛给 Parent,第二代 Child 将 fling 转化为 scroll 后再分发给 Parent,为了和普通的 scroll 区分增加了一个参数 type, 当 type 是 ViewCompat.TYPE_TOUCH 时表示普通的 scroll,当是 ViewCompat.TYPE_NON_TOUCH 时表示由 fling 转化而来的 scroll。这样做的好处是当 Child 检测到一个 fling 时,它可以选择将这个 fling 引起的 scroll 一部分作用在 Parent 上一部分作用在自己身上,而不是只作用在 Parent 或者 Child 上。或许你会问 fling 为什么不能选择 Parent 和 Child 都作用,事实上你可以,但 fling 的话 Parent 没法告诉 Child 消费了多少,剩下多少,因为 fling 传递的值是速度,不像 scroll 是距离。所以通过 NestedScrolling(Parent2/Child2) 实现嵌套滚动时,当你触发了一个 fling 时,也可以做很顺滑连贯的交替滚动,而 1 就很难达到相同的效果。现在官方 View 的实现也是通过 NestedScrolling(Parent2/Child2),所以我们在实现自定义的嵌套滚动时尽量用 2。

上面简单介绍了 NestedScrolling 2 和 1 的区别以及为什么要使用2。现在我们来看看 NestedScrolling(Parent2/Child2) 的方法,1 就不看了,和 2 差不多。

public interface NestedScrollingChild2 {

void setNestedScrollingEnabled(boolean enabled);

boolean isNestedScrollingEnabled();

boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type);

void stopNestedScroll(@NestedScrollType int type);

boolean hasNestedScrollingParent(@NestedScrollType int type);

boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,

int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow,

@NestedScrollType int type);

boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,

@Nullable int[] offsetInWindow, @NestedScrollType int type);

}

public interface NestedScrollingParent2 {

boolean onStartNestedScroll(@NonNull View child, @NonNull View target, @ScrollAxis int axes,

@NestedScrollType int type);

void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes,

@NestedScrollType int type);

void onStopNestedScroll(@NonNull View target, @NestedScrollType int type);

void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,

int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type);

void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,

@NestedScrollType int type);

}

从这两个接口的方法可以看出这些方法都是一一对应的,比如 startNestedScroll 和 onStartNestedScroll,stopNestedScroll 和 onStopNestedScroll 等。从这些方法的命名上也能看出来嵌套滚动的交互顺序是 Child 主动触发,Parent 被动接受,所以决定是否打开嵌套滚动的方法 setNestedScrollingEnabled 由 Child 实现,决定开始和结束的方法 startNestedScroll 和 stopNestedScroll 也由 Child 实现。

这里用一个图来表示嵌套滚动流程

整个过程大概分为两部分:绑定和滚动分发。绑定部分可以理解为 Child 向上遍历找 NestedScrollingParent2 的过程,找到后调用它的 onStartNestedScroll 方法,如果返回 true 则说明这个 Parent 想接收 nested scroll,Child 会紧接着调 onNestedScrollAccepted 方法表示同意 Parent 处理自己分发的 nested scroll,对应上图中的 1 2 3。滚动分发部分 Child 将自己的 scroll 分为三个阶段 before scroll after,before 和 after 分发给 parent 消费,scroll 阶段让自己消费,这三个阶段是按顺序进行的,换句话说如果前一步消耗完了 scroll,那后面的阶段就没有 scroll 可以消费。这样做的好处是让 Parent 可以在自己消费之前或者之后消费 scroll,如果 Parent 想在 Child 之前消费就在 onNestedPreScroll 方法里处理,否则就在 onNestedScroll 方法里,对应上图中的 4 5 步。上面介绍到的一些通用逻辑被封装在 NestedScrollingChildHelper 和 NestedScrollingParentHelper 中,在 NestedScrolling(Parent2/Child2) 的方法中可以调用 Helper 类中的同名方法,比如 NestedScrollingChild2.startNestedScroll 方法中实现了向上遍历寻找 NestedScrollingParent 的逻辑。

三级嵌套滚动

一个常见的嵌套滚动例子是 CoordinatorLayout/AppbarLayout - RecyclerView, 实现的效果是向上滑动列表时,会先将 AppbarLayout 向上滑动直到完全折叠,向下滑动至列表最顶部后会展开 AppbarLayout, 如下图:

这里实现 NestedScrollingParent2 的是 CoordinatorLayout/AppbarLayout, 实现 NestedScrollingChild2 的是 RecyclerView。对于这种两级嵌套滚动的需求使用 CoordinatorLayout 几乎都能实现,如果遇到特殊的业务需求基于 CoordinatorLayout 和 RecyclerView 的实现改改也能实现。

我这里遇到的需求是即刻首页的样式(可参考即刻5.4.2版本),除了要有 AppbarLayout 折叠效果之外还要在 AppbarLayout 顶部展示搜索框和刷新动画。这里的滑动逻辑是:

向上滑动时,最先折叠刷新动画,向下滑动时最后展开刷新动画。

向上滑动列表时先折叠 AppbarLayout,AppbarLayout 完全折叠后再折叠搜索框。

向下滑动列表时在展开 AppbarLayout 之前先展开搜索框。

列表没滑动到顶部时可以通过触发一定速度的向下 fling 来展开搜索框。

可以发现这里除了 CoordinatorLayout/AppbarLayout - RecyclerView 这对嵌套滚动的 Parent 和 Child 之外还多了搜索框和刷新动画,而这三者之间的滑动逻辑需要通过嵌套滚动实现,只是传统的两级嵌套滚动不能满足,所以需要实现三级嵌套滚动。

所谓三级嵌套滚动是在两级嵌套滚动之上再添加一个 Parent,这里为了表述方便将三级嵌套滚动的三级由上到下分别称为 Grand Parent Child。具体是由两对 NestedScrolling(Parent2/Child2) 接口实现,Grand 实现第一对接口的 Parent,Parent 实现第一对接口的 Child 和第二对接口的 Parent,Child 实现第二对接口的 Child。与两级嵌套滚动相比三级嵌套的 Grand 和 Child 和两级的 Parent 和 Child 区别不大,变化比较大的是三级的 Parent 既要实现两级的 Parent 接口又要实现 Child 接口,示意图如下:

在即刻首页这个例子里,CoordinatorLayout/AppbarLayout 属于三级嵌套的 Parent 实现了第二对接口的 NestedScrollingParent2,RecyclerView 属于 Child 实现了第二对接口的 NestedScrollingChild2。这里我们需要做的是实现第一对嵌套接口,新建一个自定义 Layout 实现 NestedScrollingParent2 接口作为三级嵌套的 Grand,负责搜索框和刷新动画的折叠和展开。再新建一个自定义 Layout 继承 CoordinatorLayout 实现 NestedScrollingChild2 接口,负责拦截列表分发上来的滚动事件或者处理 AppbarLayout 消费后剩下的滚动事件。

二级嵌套滚动可以理解为给 Parent 提供了拦截 Child 滚动事件和处理 Child 剩余滚动事件的能力,具体逻辑可参考本文最开始介绍嵌套滚动的部分。相应的三级嵌套滚动给 Grand 提供了拦截 Parent 和处理剩余滚动事件的能力,只是拦截和处理的时机多了一些,如下图:

二级嵌套滚动对滚动处理时机只有三个阶段:preScroll、scroll 和 afterScroll。而三级嵌套滚动的处理时机就多一些,有七个阶段:prePreScroll、preScroll、afterPreScroll、scroll、preAfterScroll、afterScroll 和 afterAfterScroll,可以看出相比二级嵌套多了 prePreScroll、afterPreScroll、preAfterScroll 和 afterAfterScroll 这四个阶段,多出的这几个阶段都是给 Grand 用的。到这里可以发现 NestedScrollingParent2 其实不能完全描述 Grand 的能力,确实最理想的方案应该是新建一对接口 NestedScrollingGrand2 和 NestedScrollingGrandChild2 来描述新增的四个对滚动事件的处理阶段,但考虑到我这里的例子 Grand 对 Parent 的处理没有那么精细化,所以还是通过复用 NestedScrolling(Parent2/Child2) 和一些附加方法来实现。以后如果实现了 NestedScrolling(Grand2/GrandChild2) 接口,也会及时更新。根据上图即刻首页滑动的实现思路就很简单了:

onPrePreScroll 中执行折叠刷新动画的逻辑,onAfterAfterScroll 中执行展开刷新动画的逻辑。

onPreScroll 中执行折叠 AppbarLayout 的逻辑,onAfterPreScroll 中执行搜索框折叠的逻辑。

onAfterScroll 中执行展开 AppbarLayout 的逻辑,onPreAfterScroll 中执行搜索框展开的逻辑。

列表没滑到顶部根据 fling 展开搜索框的逻辑单独在 Parent 的 onNestedPreFling 里做,这条算是一个特殊处理。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

android嵌套组合动画,Android 三级NestedScroll嵌套滚动实践相关推荐

  1. android tab 切换动画,Android之ViewPager+TabLayout组合实现导航条切换效果(微信和QQ底部多标签切换)...

    前言 之前在另外一篇中用Fragment和button实现了点击切换Fragment的效果,比较简陋.这次改用ViewPager+TabLayout 实现联动的效果. 实现效果 ViewPager 多 ...

  2. android常用窗口动画,android 自定义dialog,窗口动画,

    自定义dialog窗口,根据坐标可随意设置dialog显示位置,实现了窗口弹出动画 Java代码: package com.sunxu.org.IndividualityDialog; import ...

  3. android view交替动画,Android View原理(View树遍历,View重绘,View动画)

    一.屏幕绘图基础 Android中的GUI系统是客户端和服务端配合的窗口系统,即后台运行了一个绘制服务,每个应用程序都是该服务端的一个客户端,当客户端需要绘制时,首先请求服务端创建一个窗口,然后在窗口 ...

  4. android 缩放透明动画,Android之高仿QQ6.6.0侧滑效果(背景动画、透明+沉浸式状态栏、渐变效果)...

    根据需求实现类似QQ侧滑效果,之前看到过很多实现方式通过SlidingMenu,但是既然官方推出了自己的专属控件,那么使用DrawerLayout就是不二选择.且看下文. 一.先来看看官方文档解释 D ...

  5. android dialog 消失动画,android 自定义dialog弹出和消失缩放动画

    本文转自:android 自定义dialog,窗口动画 Java代码: package com.sunxu.org.IndividualityDialog; import Android.app.Ac ...

  6. android 上下扫描动画,Android扫描雷达动画

    很简单的一个组合动画,用好基本动画啥子效果都不怕 老规矩先上图 效果图.gif ok 来 既然往下翻那就看看如何实现的吧 首先效果分为两部分 第一部分中间指针(其实这里就是一张图片) 第二部分就是波纹 ...

  7. android studio实现动画,android studio上的基本动画实现(第一篇)

    hello,各位小伙伴们,在不少小伙伴们刚刚开始学习android的时候,经常会有一些project里面须要有一些基本动画的插入,那么具体是要怎么实现呢?咱们接下一块儿分析一下在android中的几种 ...

  8. android 心跳效果动画,Android 心跳动画

    直接上代码  MainActivity public class MainActivity extends AppCompatActivity { private ImageView ivHart; ...

  9. android+桌面文件夹动画,Android动画

    1.为什么要说动画? 动画的适用是Android开发常用的知识 种类繁多,适用复杂,很多实现需要自定义动画 2.目前Android中有多少种动画? 视图动画(View 动画) 属性动画 揭露动画(Re ...

最新文章

  1. jQuery.fly插件实现添加购物车抛物线效果
  2. [UWP] 用 AudioGraph 来增强 UWP 的音频处理能力——AudioFrameInputNode
  3. 理解和配置Out of memory: Kill process
  4. Backnbone的入门基础——Backbone的model
  5. 【运营干货】三分钟,读懂互联网运营
  6. 【Matplotlib】【Python】如何使用matplotlib绘制散点图
  7. shell连接mysql
  8. Excel通过身份证获取出生年月,性别,年龄,生肖,星座,省份等信息总结归纳...
  9. c++ 输出格式控制
  10. spark TF-IDF入门
  11. 博文视点学院直播:如何用产品思维解决生活中的迷茫
  12. c语言this什么意思,JavaScript 中的this是什么?它到底做了什么?
  13. jQuery each()跳出循环
  14. 华为OD机试 - 消消乐游戏(Java JS Python)
  15. Ubuntu Linux 系统 键盘错乱 ,按键与输出不一致
  16. 【计算机网络系列】网络概述与体系结构
  17. 计算机专业新老生交流会ppt,新老生交流会的经典发言稿范文
  18. ef mysql 事务_EF中使用事务 - 李超明的个人空间 - OSCHINA - 中文开源技术交流社区...
  19. Android APP 定时提醒
  20. 图解大数据 | 分布式平台Hadoop与Map-Reduce详解

热门文章

  1. 关于美能达Autocord
  2. TRS电信114企业搜索引擎解决方案
  3. Excel教程中数组公式之系列教程(一)
  4. 云谷计算机华南,广州南沙云谷机房,广州南沙云谷数据中心
  5. windows11 MySql服务不见了
  6. Operating System Not Found解决方案
  7. java 正则表达式 (本文系转载,百度了一下,没找到原博客,或者作者,找了一篇2008年的贴过来了╮(╯▽╰)╭)
  8. 【C/C++服务器开发】事件驱动、事件驱动架构、事件驱动编程及设计模式
  9. conda 添加清华源
  10. c#线程中的属性isbackground