Android嵌套滑动机制分析

简介: Android嵌套滑动机制分析

本文大纲



1. 嵌套ScrollView同向滑动


2. 传统事件分发和嵌套滑动事件分发


3. NestedScrollingChild和NestedScrollingParent


4. NestedScrollingChildHelper和NestedScrollingParentHelper


5. 结合案例讲解嵌套滑动事件分发顺序


1. 嵌套ScrollView同向滑动


所谓嵌套ScrollView同向滑动,是指两个可滑动的View内外嵌套,并且他们的方向是相同的

da9c929ff97e6d28257f005d4d2c0cbd.jpg

当在内部ScrollView中上下滑动时,会有两种情况。


  1. 外部ScrollView优先获取上下滑动的权力,在蓝色区域上下滑动,内部ScrollView并不会上下滑


image.png

内部的ScrollView优先获取上下滑动的权力,在蓝色区域内上下滑动,内部ScrollView上下滑动

image.png

内部的ScrollView优先获取上下滑动的权力,在蓝色区域内上下滑动,内部ScrollView上下滑动


image.png

在Android未提供嵌套滑动机制之前,嵌套ScrollView同向滑动的效果是情况一,外部的ScrollView优先获得上下滑动的权力。原因如下:


当外部ScrollView嵌套内部ScrollView时,DOWN事件在内部ScrollView的onTouchEvent中返回true,内部ScrollView处理了DOWN事件。MOVE事件首先到达外部ScrollView的onInterceptTouchEvent方法,当滑动距离大于mTouchSlop时,会拦截掉MOVE事件,给内部ScrollView发出CANCEL事件,从而外部ScrollView获得了事件的处理权,内部ScrollView失去了事件的处理权。

ca5fd8e1961b7c0c601c5da56d418db0.jpg

这种效果显然不能满足用户的需求。内部的ScrollView永远无法获得上下滑动的机会。用户希望在内部ScrollView的区域上下滑动时,内部ScrollView优先获得事件的分发权力。Android嵌套滑动机制,很好的解决了ScrollView嵌套时内部ScrollView无法优先处理滑动事件的问题。


2. 传统事件分发和嵌套滑动事件分发


传统事件分发在Android事件分发之树的深度遍历分析法中已经详细讲解过,这里不做详细的讲解。事件会调用父控件的onInterceptTouchEvent方法,判断是否拦截事件。换言之传统的事件分发,生杀大权掌握在父控件上,只有父控件不拦截事件,子控件才有处理事件的机会。


传统事件分发树形图如下

byte_station_传统事件分发树形图.png

而嵌套滑动事件分发则区别于传统事件分发,事件分发的生杀大权并不掌握在父控件上。当DOWN事件传递到NestedScrollingChild时,NestedScrollingChild控件会向上寻找愿意响应后代View发出嵌套滑动请求的NestedScrollingParent控件,告诉NestedScrollingParent控件,后续的MOVE事件,不要再拦截,事件优先交给NestedScrollingChild处理,如果MOVE滑动超过mTouchSlop阈值,NestedScrollingChild向上请求"不拦截事件"->“parent.requestDisallowInterceptTouchEvent(true)”,从此NestedScrollingChild的所有父控件,对事件全部不拦截,直达NestedScrollingChild控件。


嵌套滑动事件分发树形图如下

byte_station_嵌套滑动事件分发树形图.png

总结:传统事件分发,父View会优先获取事件的分发权,并且如果事件被某个View处理了,那么它的父View无法获得事件分发的机会。嵌套滑动事件分发,NestedScrollingChild处理DOWN事件时,会告诉它的NestedScrollingParent,后续的MOVE、UP等事件,NestedScrollingParent不拦截,直接交由NestedScrollingChild处理,而且它的父View有机会再次获得事件分发的机会。


3. 嵌套滑动接口



嵌套滑动机制提供了NestedScrollingChild和NestedScrollingParent接口。

byte_station_NestedScrollingChild.jpg

NestedScrollingChild方法以及作用


控制嵌套滑动机制是否开启->NestedScrollingChild#setNestedScrollingEnable和NestedScrollingChild#isNestedScrollingEnable。


通知NestedScrollingParent不拦截MOVE、UP等事件->NestedScrollingChild#startNestedScroll。


处理滑动事件之前,将滑动事件的处理权交由NestedScrollingParent处理->(NestedScrollingChild#dispatchNestedPreScroll)。(这里叨唠一下,子View虽然优先获取了事件分发的权力,但是在滑动之前,还是会把事件交给父View去处理。传统事件分发,父View如果获取了分发权,子View就无法获取分发权。滑动嵌套即使子View获取了分发权,它还是会征询父View,是否要处理掉事件的一部分滑动,父View可以选择处理或者不处理,子View再处理事件剩余滑动距离)。


NestedScrollingChild处理完滑动事件后,如果有剩余的滑动距离没有处理掉,会交给NestedScrollingParent去处理剩余的滑动距离->NestedScrollingChild#dispatchNestedScoll,如果NestedScrollingParent处理了剩余的距离,NestedScrollingChild会校正mLastMotionY。


dispatchNestedPreFling方法与dispatchNestedPreScroll方法类似。当手指离开屏幕,NestedScrollingChild会将Fling的处理权先交给NestedScrollingParent处理。


dispatchNestedFling方法与dispatchNestedScroll方法类似。如果有剩余的滑动Fling距离没有处理掉,会交给NestedScrollingParent去处理剩余的Fling距离


NestedScrollingChild事件处理步骤


获取事件的优先处理权-> 调用startNestedScroll


处理滑动之前先问NesteScrollingParent要不要处理一部分距离


NesteScrollingParent处理完之后,NestedScrollingChild再来处理剩余的距离4.NestedScrollingChild处理完之后还有剩余的距离,交给NesteScrollingParent去处理

byte_station_NestedScrollingParent.jpg

NestedScrollingParent接口只能被ViewGroup实现。View实现该接口没有意义。NestedScrollingParent主要有两个作用。作用一、响应NesteScrollingChild的嵌套滑动请求,配合NesteScrollingChild完成嵌套滑动机制的交互。作用二、处理NestedScrollingChild滑动前和滑动后传递过来的事件剩余距离。


NestedScrollingParent方法以及作用


getNestedScrollAxes返回嵌套滑动的方向,垂直方向或者水平方向。NestedScrollingParent正是通过该标志位,决定要不要拦截MOVE事件


onStartNestedScroll():Boolean方法与NestedScrollingChild#startNestedScroll方法相对应。如果返回true表示NestedScrollingParent响应NestedScrollingChild的嵌套滑动,反之表示不响应嵌套滑动。一般做法是外部ScrollView滑动方向和内部ScrollView滑动方向一致返回true。


onNestedScrollAccepted在onStartNestedScroll返回true时,会被调用,该方法体会给mNestedScrollAxes赋值。


onNestedPreScroll方法会在NestedScrollingChild正式处理滑动事件之前调用。


onNestedScroll方法会在NestedScrollingChild正式处理滑动事件之后调用


onNestedPreFling与onNestedPreScroll类似


onNesteFling与onNestedScoll类似


onStopNestedScroll会将mNestedScrollAxes置为0


4. NestedScrollingChildHelper和NestedScrollingParentHelper



帮助类方法比较多,实现类嵌套滑动的核心流程的具体算法。刚接触难免觉得容易混淆。本章不做具体代码讲解,请自行查阅源码。

NestedScrollingChildHelper类是NesteScrollingChild的帮助类。

byte_station_NestedScrollingChildHelper.jpg

byte_station_NestedScrollingParentHelper.jpg

NestedScrollingChildHelper#startNestedScroll。从父View开始向祖先View遍历,寻找到第一个愿意响应嵌套滑动的NestedScrollingParent。

NestedScrollingChildHelper#dispatchNestedPreScroll。如果NestedScrollingParent消耗了部分滑动距离,返回true,否则返回false。


5.结合案例讲解嵌套滑动事件分发顺序


image.png

上述效果代码如下


自定义的ScrollView,主要是在相关事件方法中加入日志打印

byte_station_自定义ScrollView2.jpg

Activity代码

byte_station_Activity代码.jpg

布局文件

byte_station_布局文件.jpg

我们把布局文件转化成树形图

ffe738f458d0a138b9e4d3402b3e3e85.png

关于树中的节点,介绍如下

f1e20f6e54949b9ef3cd635b32b773b1.png

日志打印如下

ebf1213c1e17a3cb9c1f18820994a781.jpg

OuterScrollView拦截MOVE事件条件:滑动距离>mTouchSlop&&OuterScrollView未响应嵌套滑动机制。


DOWN事件到达OuterScrollView#onInterceptTouchEvent,不拦截。


DOWN事件到达InnerScrollView#onInterceptTouchEvent,接着调用InnerScrollView#startNestedScroll,触发OuterScrollView#onStartNested,接着调用InnerScrollView#dispatchTouchEvent,由于是DOWN事件,会调用View#dispatchTouchEvent,调用stopNestedScroll,触发OuterScrollView#onStopNestedScroll。


DOWN事件到达InnerScrollView#onTouchEvent方法,调用startNestedScroll,触发OuterScrollView#onStartNestedScroll,OuterScrollView响应嵌套滑动机制。


MOVE事件到达OuterScrollView#onInterceptTouchEvent,拦截条件不成立,不拦截。


MOVE事件到达InnerScrollView#onTouchEvent,调用InnerScrollView#dispatchNestedPreScroll,由于dy=0 dx=0,不会触发OuterScrollView的onNestedPreScroll。


MOVE事件到达OuterScrollView#onInterceptTouchEvent,拦截条件不成立,不拦截。


MOVE事件到达InnerScrollView#onTouchEvent,dy<mTouchSlop,只会触发InnerScrollView#dispatchNestedPreScroll,不会触发dispatchNestedScroll。


MOVE事件到达OuterScrollView#onInterceptTouchEvent,拦截条件不成立,不拦截。


MOVE事件到达InnerScrollView#onTouchEvent,dy<mTouchSlop,只会触发InnerScrollView#dispatchNestedPreScroll,不会触发dispatchNestedScroll。


MOVE事件到达OuterScrollView#onInterceptTouchEvent,拦截条件不成立,不拦截。


MOVE事件到达InnerScrollView#onTouchEvent,dy<mTouchSlop,只会触发InnerScrollView#dispatchNestedPreScroll,不会触发dispatchNestedScroll。


MOVE事件到达OuterScrollView#onInterceptTouchEvent,拦截条件不成立,不拦截。


MOVE事件到达InnerScrollView#onTouchEvent,(dy=31)>mTouchSlop,会同时触发InnerScrollView#dispatchNestedPreScroll和dispatchNestedScroll,同时会调用parent.requestDisallowInterceptTouchEvent(true)。从此MOVE事件直达InnerScrollView#onTouchEvent


MOVE事件直达InnerScrollView#onTouchEvent


MOVE事件直达InnerScrollView#onTouchEvent


处理UP事件


总结DOWN事件和MOVE事件在NestedScrollingChild中的分发逻辑


1.DOWN事件到达NestedScrollingChild#onInterceptTouchEvent方法会调用startNestedScroll方法。


20DOWN事件到达NestedScrollingChild#dispathTouchEvent方法会调用stopNestedScroll方法。


3.MOVE事件到达NestedScrollingChild#onTouchEvent方法,调用dispatchNestedPreScroll方法。如果mIsBeingDragged为true。会调用dispatchNestedScroll方法。从非dragged状态变成dragged状态时,会调用ViewParent#requestDisallowInterceptTouchEvent(true)。




相关文章
|
24天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
34 2
|
5天前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
|
12天前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
34 8
|
12天前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
29天前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
27 1
|
2月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着重要的位置。本文将深入探讨这两种操作系统的开发环境,从编程语言到开发工具,从用户界面设计到性能优化,以及市场趋势对开发者选择的影响。我们旨在为读者提供一个全面的比较视角,帮助理解不同平台的优势与挑战,并为那些站在选择十字路口的开发者提供有价值的参考信息。
|
2月前
|
开发框架 Android开发 Swift
安卓与iOS应用开发对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文将深入探讨这两大操作系统在开发环境、编程语言、用户界面设计、性能优化及市场分布等方面的差异和特点。通过比较分析,旨在为开发者提供一个宏观的视角,帮助他们根据项目需求和目标受众选择最合适的开发平台。同时,文章还将讨论跨平台开发框架的利与弊,以及它们如何影响着移动应用的开发趋势。
|
2月前
|
安全 搜索推荐 Android开发
安卓与iOS应用开发的对比分析
【8月更文挑战第20天】在移动应用开发领域,安卓和iOS两大平台各领风骚。本文通过深入探讨两者的开发环境、编程语言、用户界面设计、应用市场及分发机制等方面的差异,揭示了各自的优势和挑战。旨在为开发者提供决策支持,同时帮助理解为何某些应用可能优先选择在一个平台上发布。
29 2
|
Android开发 安全
Android应用内广播LocalBroadcastManager机制详解
终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~ 1. 简介 通常我们在使用Android广播的时候都会直接将广播注册到系统的AMS当中,由于AMS任务繁忙,一般可能不会立即能处理到我们发出的广播,如果我们使用广播是在应用内的单个进程中使用,则完全可以采用LocalBroadcastManager来处理。
1293 0
|
4天前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。