NestedScrollView嵌套滑动源码解读

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;

}

复制代码

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

滑翔是一个互斥处理的过程,而滑动是一个接力的过程

2.3.6 滑动结束通知

public void stopNestedScroll() {

stopNestedScroll(TYPE_TOUCH);

}

public void stopNestedScroll(@NestedScrollType int type) {

ViewParent parent = getNestedScrollingParentForType(type);

if (parent != null) {

// 通知嵌套父容器,滑动结束

ViewParentCompat.onStopNestedScroll(parent, mView, type);

setNestedScrollingParentForType(type, null); // 清理父容器引用

}

}

复制代码

3、NestedScrollingParentHelper类


作为嵌套滑动的父容器角色,其只有接受通知时处理即可,情况没有子视图角色那么复杂;而辅助类里仅仅是对滑动方向做了声明周期处理;

成员变量

private int mNestedScrollAxesTouch; // Touch事件时,接受处理时,事件的滑动方法

private int mNestedScrollAxesNonTouch; // 非Touch事件时,接受处理时,事件的滑动方法

复制代码

3.1 滑动方向获取

public int getNestedScrollAxes() {

return mNestedScrollAxesTouch | mNestedScrollAxesNonTouch;

}

复制代码

3.2 滑动方向设置

public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,

@ScrollAxis int axes) {

onNestedScrollAccepted(child, target, axes, ViewCompat.TYPE_TOUCH);

}

public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,

@ScrollAxis int axes, @NestedScrollType int type) {

if (type == ViewCompat.TYPE_NON_TOUCH) {

mNestedScrollAxesNonTouch = axes;

} else {

mNestedScrollAxesTouch = axes;

}

}

复制代码

3.3 滑动方向重置

public void onStopNestedScroll(@NonNull View target) {

onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);

}

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

if (type == ViewCompat.TYPE_NON_TOUCH) {

mNestedScrollAxesNonTouch = ViewGroup.SCROLL_AXIS_NONE;

} else {

mNestedScrollAxesTouch = ViewGroup.SCROLL_AXIS_NONE;

}

}

复制代码

4、嵌套实现机制


作为一是具有兼容性实现的嵌套滑动容器,它必须实现下面接口

  • 滑动容器接口ScrollingView

  • 嵌套滑动父容器接口NestedScrollingParent3

  • 嵌套滑动子视图接口NestedScrollingChild3

嵌套接口,可以根据容器角色选择实现;方法实现需要利用辅助类

从上面对两个辅助类解读;对他们已经实现的功能做了归纳

  1. 嵌套是否支持

  2. 嵌套通知

  3. 嵌套滑动方向

也就是作为子视图角色的实现方法基本使用辅助类即可,而嵌套父容器角色需要我们增加实现逻辑;需要实现从功能上划分:

  1. 作为嵌套子视图设置,

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

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

总结:

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。

  • BAT大厂面试题、独家面试工具包,

  • 资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter,


9002286)]
[外链图片转存中…(img-LeboakUg-1711939002286)]
[外链图片转存中…(img-5hMxpeSv-1711939002287)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-5rfyVDIi-1711939002287)]

总结:

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。

  • BAT大厂面试题、独家面试工具包,

  • 资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter,

    [外链图片转存中…(img-65OStjwI-1711939002287)]
    [外链图片转存中…(img-m3hmQhxX-1711939002288)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 13
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值