NestedScrollView嵌套滑动源码解读,Android面试必问的HashMap

本文详细介绍了NestedScrollingChildHelper和NestedScrollingParentHelper类在Android中的作用,包括它们如何处理嵌套滚动、事件通知时机、父子视图间的交互以及关键方法如setNestedScrollingEnabled和dispatchNested*方法的用法。
摘要由CSDN通过智能技术生成

这篇内容分三个小章节

  1. NestedScrollingChildHelper类

  2. NestedScrollingParentHelper类

  3. 实现处理以及调用时机

在这里类的解读是必须的,不然只能死记其调用时机,这里是不建议的;下面会贴一部分源码,在源码中会对代码的一些关键进行注释说明

2、NestedScrollingChildHelper类


嵌套子视图角色;主要功能

  • 事件是否需要通知

  • 事件通知

类中如下变量:

private ViewParent mNestedScrollingParentTouch; // touch事件接力的父容器

private ViewParent mNestedScrollingParentNonTouch; // 非touch事件接力的父容器

private final View mView; // 当前容器,也是作为嵌套滑动时孩子角色的容器

private boolean mIsNestedScrollingEnabled; // 当前容器是否支持嵌套滑动

private int[] mTempNestedScrollConsumed; // 二维数组,保存x、y消耗的事件长度;减少对象生成的

复制代码

2.1 实例获取

public NestedScrollingChildHelper(@NonNull View view) {

mView = view;

}

复制代码

2.2 嵌套滑动支持

是对嵌套子视图的角色来说的

public void setNestedScrollingEnabled(boolean enabled) {

if (mIsNestedScrollingEnabled) {

ViewCompat.stopNestedScroll(mView); // 兼容模式调用

}

mIsNestedScrollingEnabled = enabled;

}

public boolean isNestedScrollingEnabled() {

return mIsNestedScrollingEnabled;

}

复制代码

2.3 嵌套滑动相关方法

要支持嵌套滑动,那么必须有多个支持嵌套滑动的容器;作为子视图,其需要有通知的一套,因此方法有:

  • 父容器的查找、判断

  • 通知开始、过程以及结束

2.3.1 嵌套父容器的查找

成员变量mNestedScrollingParentTouch、mNestedScrollingParentNonTouch为父容器缓存变量;其直接设置和获取方法如下

private ViewParent getNestedScrollingParentForType(@NestedScrollType int type) {

switch (type) {

case TYPE_TOUCH:

return mNestedScrollingParentTouch;

case TYPE_NON_TOUCH:

return mNestedScrollingParentNonTouch;

}

return null;

}

private void setNestedScrollingParentForType(@NestedScrollType int type, ViewParent p) {

switch (type) {

case TYPE_TOUCH:

mNestedScrollingParentTouch = p;

break;

case TYPE_NON_TOUCH:

mNestedScrollingParentNonTouch = p;

break;

}

}

复制代码

2.3.2 嵌套父容器的支持判断

public boolean hasNestedScrollingParent() {

return hasNestedScrollingParent(TYPE_TOUCH);

}

public boolean hasNestedScrollingParent(@NestedScrollType int type) {

return getNestedScrollingParentForType(type) != null;

}

复制代码

2.3.3 滑动开始通知

public boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type) {

if (hasNestedScrollingParent(type)) {

return true;

}

if (isNestedScrollingEnabled()) { // 孩子视图支持嵌套滑动,只有支持才会继续执行

ViewParent p = mView.getParent();

View child = mView;

while (p != null) { // 查找的不仅仅直接父容器

// 兼容调用,父容器是否可以作为嵌套父容器角色

if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)) {

setNestedScrollingParentForType(type, p); // 这里进行了缓存

// 兼容调用,父容器

ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes, type);

return true;

}

if (p instanceof View) {

child = (View) p;

}

p = p.getParent();

}

}

return false;

}

复制代码

父容器的查找,采取了延时策略,在进行事件时,才进行查询,并且在查询到了,进行支持;所以可以这样理解:

  1. onStartNestedScroll:是父容器接受事件通知方法,其结果表示是否可以作为嵌套滑动的父容器角色

  2. onNestedScrollAccepted:不是必调用,调用了表明嵌套父容器角色支持view的后续嵌套处理

2.3.4 手指滑动通知

滑动时通知,分为滑动前和滑动后;使嵌套滑动处理更灵活 滑动前通知

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

@Nullable int[] offsetInWindow) {

return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, TYPE_TOUCH);

}

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

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

if (isNestedScrollingEnabled()) {

final ViewParent parent = getNestedScrollingParentForType(type);

if (parent == null) {

return false;

}

if (dx != 0 || dy != 0) {

int startX = 0;

int startY = 0;

if (offsetInWindow != null) {

mView.getLocationInWindow(offsetInWindow);

startX = offsetInWindow[0];

startY = offsetInWindow[1];

}

if (consumed == null) {

consumed = getTempNestedScrollConsumed();

}

consumed[0] = 0;

consumed[1] = 0;

ViewParentCompat.onNestedPreScroll(parent, mView, dx, dy, consumed, type);

if (offsetInWindow != null) {

mView.getLocationInWindow(offsetInWindow);

offsetInWindow[0] -= startX;

offsetInWindow[1] -= startY;

}

return consumed[0] != 0 || consumed[1] != 0;

} else if (offsetInWindow != null) {

offsetInWindow[0] = 0;

offsetInWindow[1] = 0;

}

}

return false;

}

复制代码

其中两个二维数组作为结果回传;通过父容器的onNestedPreScroll方法进行处理并把滑动处理详情放入两个二维数组中,常用的详情为消耗长度情况;返回结果表示滑动前是否处理

滑动后通知

public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,

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

return dispatchNestedScrollInternal(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,

offsetInWindow, TYPE_TOUCH, null);

}

public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,

int dyUnconsumed, @Nullable int[] offsetInWindow, @NestedScrollType int type) {

return dispatchNestedScrollInternal(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,

offsetInWindow, type, null);

}

public void dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,

int dyUnconsumed, @Nullable int[] offsetInWindow, @NestedScrollType int type,

@Nullable int[] consumed) {

dispatchNestedScrollInternal(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,

offsetInWindow, type, consumed);

}

private boolean dispatchNestedScrollInternal(int dxConsumed, int dyConsumed,

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

@NestedScrollType int type, @Nullable int[] consumed) {

if (isNestedScrollingEnabled()) {

final ViewParent parent = getNestedScrollingParentForType(type);

if (parent == null) {

return false;

}

if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {

int startX = 0;

int startY = 0;

if (offsetInWindow != null) {

mView.getLocationInWindow(offsetInWindow);

startX = offsetInWindow[0];

startY = offsetInWindow[1];

}

if (consumed == null) {

consumed = getTempNestedScrollConsumed();

consumed[0] = 0;

consumed[1] = 0;

}

ViewParentCompat.onNestedScroll(parent, mView,

dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed);

if (offsetInWindow != null) {

mView.getLocationInWindow(offsetInWindow);

offsetInWindow[0] -= startX;

offsetInWindow[1] -= startY;

}

return true;

} else if (offsetInWindow != null) {

offsetInWindow[0] = 0;

offsetInWindow[1] = 0;

}

}

return false;

}

复制代码

其中两个二维数组作为结果回传;通过父容器的onNestedScroll方法进行处理并把滑动处理详情放入两个二维数组中,常用的详情为消耗长度情况;返回结果表示滑动前是否处理

2.3.5 滑翔通知

滑翔也有两个时机

滑翔前

public boolean dispatchNestedPreFling(float velocityX, float velocityY) {

if (isNestedScrollingEnabled()) {

ViewParent parent = getNestedScrollingParentForType(TYPE_TOUCH);

if (parent != null) {

return ViewParentCompat.onNestedPreFling(parent, mView, velocityX,

velocityY);

}

}

return false;

}

复制代码

返回结果表明父容器的是否处理滑翔;父容器是通过onNestedPreFling进行处理

滑翔后

public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {

if (isNestedScrollingEnabled()) {

ViewParent parent = getNestedScrollingParentForType(TYPE_TOUCH);

if (parent != null) {

return ViewParentCompat.onNestedFling(parent, mView, velocityX,

velocityY, consumed);

}

}

return false;

}

复制代码

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

d);

}

}

return false;

}

复制代码

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-YCPOqINP-1710927912999)]
[外链图片转存中…(img-Zxgi0cF6-1710927913000)]
[外链图片转存中…(img-S2yS4IDe-1710927913000)]
[外链图片转存中…(img-9rr9CsCy-1710927913001)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-DvLC7s7R-1710927913001)]

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android应用码45套安卓码合集: android中文离线发音引擎FOCTTS使用码.rar Android应用码(精)LBS签到应用码.rar Android应用码(精)xUtils2.2.5.rar Android应用码(精)仿博客园客户端码.rar Android应用码(精)手机控制电脑鼠标.rar Android应用码(精)记事本小程序,加注释,适合阅读.rar Android应用Android平台下通过HTTP协议实现断点续传下载.rar Android应用码Hibernate4Android.rar Android应用码http、udp、tcp网络交互组件.rar Android应用码ListView实现的目录树结构.rar Android应用码SdCard读写文件实例.rar Android应用码SlidingMenu使用例子.rar Android应用码串口通信(JNI)例子.rar Android应用码任务提醒码.rar Android应用码仿360手机助手首页浮动菜单.rar Android应用码仿Iphone抖动效果Shake Icon.rar Android应用码仿QQ分组列表修改版.rar Android应用码使用listView实现的树状结构.rar Android应用码俄罗斯方块注释超详细版.rar Android应用码利用poi将内容填到word模板.rar Android应用码动态列表布局.rar Android应用码单Java文件实现的计算器.rar Android应用码基于百度云推送的聊天工具码.rar Android应用码安卓多边形布局例子.rar Android应用码安卓拍照上传实现代码附带php端.rar Android应用码实现动态交叉布局.rar Android应用码小说翻页效果码.rar Android应用码广告轮播效果码.rar Android应用码强大的统计图表库.rar Android应用码微享,微信分享实例.rar Android应用码有米广告SDK例子.rar Android应用码模仿zaker风景页面滑动效果修改版.rar Android应用码水波纹动画效果.rar Android应用码泡泡效果bubble.rar Android应用码猜猜红桃A.rar Android应用码百度统计例子.rar Android应用码简单的Android图片轮播.rar Android应用码简单的仿微信实现了表情效果.rar Android应用码结合数据库进行摇一摇的实例.rar Android应用码花姑娘之部分UI码.rar Android应用码获取手机信息.rar Android应用码讯飞语音测试码.rar Android应用码飞碟说欢迎界面.rar
HashMap是Java中的一种数据结构,提供了键值对的存储和查找功能。在HashMap的底层实现中,使用了数组和链表(或者在Java 1.8中使用了红黑树)来解决哈希冲突的题。 哈希冲突指的是当不同的键对象计算出的哈希值相同时,它们需要被存储在数组的同一个位置上。为了解决哈希冲突,HashMap中使用了两种方法,分别是开放地址法和链地址法。 开放地址法是指当发生哈希冲突时,继续寻找下一个空槽位来存储键值对。这个方法需要保证数组的长度是2的幂次方,通过hash & (length-1)的位运算来减少哈希冲突的概率[2]。 链地址法是指将发生哈希冲突的键值对存储在同一个位置上的链表或红黑树中。这个方法在Java 1.8中使用,当链表的长度超过一定阈值时,会将链表转换为红黑树,以提高查找效率。 在HashMap中,put方法用于插入键值对。当调用put方法时,首先会计算键对象的哈希值,并与数组的长度取余来确定存储位置。如果该位置已经存在键值对,则根据键对象的equals方法来判断是否是同一个键,如果是,则更新对应的值,否则将新键值对插入到链表或红黑树中。如果发生哈希冲突,就会根据选择的解决冲突的方法,继续寻找下一个空槽位或者在链表或红黑树中插入键值对。如果插入后,数组中存储的键值对的数量超过了负载因子(默认为0.75),就会触发扩容操作。 扩容操作会创建一个更大的数组,并将原数组中的键值对重新计算哈希值后插入到新数组中。扩容操作会在数组大小达到阈值(数组长度乘以负载因子)时触发。 总结起来,HashMap的底层实现是通过数组和链表(或红黑树)来解决哈希冲突的题。它使用哈希值计算和位运算来确定存储位置,同时使用开放地址法和链地址法来解决哈希冲突。在插入键值对时,需要计算哈希值、确定存储位置,并根据解决冲突的方法进行插入。当数组中的键值对数量超过负载因子时,会触发扩容操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [HashMap 底层解读(一行一行读,有基础就能看懂)](https://blog.csdn.net/rain67/article/details/124043769)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值