2024年安卓最新NestedScrollView嵌套滑动源码解读,面试的时候突然遇到答不上的问题怎么办

最后

文章所有资料全部已经打包整理好,另外小编手头上整理了大量Android架构师全套学习资料,Android核心高级技术PDF文档+全套高级学习资料+视频+2021 BAT 大厂面试真题解析

资料展示:

image

image

image

image

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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. 作为嵌套子视图设置,

  2. 作为嵌套父容器的实现

  3. 滑动接力处理,以及滑翔处理

4.1 嵌套子视图支持

构造器中进行setNestedScrollingEnabled(true)方法进行设置

setNestedScrollingEnabled方法

public void setNestedScrollingEnabled(boolean enabled) {

mChildHelper.setNestedScrollingEnabled(enabled);

}

复制代码

4.2 嵌套父容器的支持

public boolean onStartNestedScroll(

@NonNull View child, @NonNull View target, int nestedScrollAxes) {

return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);

}

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

int type) {

return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;

}

复制代码

可滑动方向判断进而决定是否支持的;支持时的处理如下

public void onNestedScrollAccepted(

@NonNull View child, @NonNull View target, int nestedScrollAxes) {

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

}

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

int type) {

mParentHelper.onNestedScrollAccepted(child, target, axes, type);

startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, type);

}

复制代码

其还是一个子视图角色,所以,其需要继续传递这个滑动开始的信号;可见嵌套默认处理中:其实是一个嵌套滑动容器链表,中间也可能存在滑动容器(不支持嵌套),链表组后一个容器的‘父’容器也还可能是嵌套滑动;这些情况造成的一个原因是同时是父容器还是子视图才会继续分发;这个链头容器必定是个嵌套子视图角色,中间即是子视图角色也是父容器角色,链尾容器必定是个嵌套父容器角色

时机

在down事件中,调用startNestedScroll方法

4.3 利用辅助类重写

下面方法利用了辅助类直接重写

  • 嵌套父容器存在判断:hasNestedScrollingParent

  • 子视图是否支持嵌套滑动:setNestedScrollingEnabled、isNestedScrollingEnabled

  • 开始通知:startNestedScroll

  • 滑动分发:dispatchNestedPreScroll、dispatchNestedScroll

  • 滑翔分发:dispatchNestedPreFling、dispatchNestedFling

  • 结束通知:stopNestedScroll

参数中涉及到滑动类型时,均采用ViewCompat.TYPE_TOUCH作为默认类型

4.4 滑动接力处理

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

onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);

}

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

int type) {

dispatchNestedPreScroll(dx, dy, consumed, null, type);

}

复制代码

其作为父容器,本身对事件并没有处理,而是作为子视图继续分发下去;时机move事件中嵌套子视图处理滑动之前

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

int dxUnconsumed, int dyUnconsumed) {

onNestedScrollInternal(dyUnconsumed, ViewCompat.TYPE_TOUCH, null);

}

private void onNestedScrollInternal(int dyUnconsumed, int type, @Nullable int[] consumed) {

final int oldScrollY = getScrollY();

scrollBy(0, dyUnconsumed);

final int myConsumed = getScrollY() - oldScrollY;

if (consumed != null) {

consumed[1] += myConsumed;

}

final int myUnconsumed = dyUnconsumed - myConsumed;

mChildHelper.dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null, type, consumed);

}

复制代码

父容器首先处理了滑动,然后把处理后的情况继续传递;时机move事件,嵌套子视图处理之后

4.5 滑翔互斥处理

public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {

return dispatchNestedPreFling(velocityX, velocityY);

}

public boolean dispatchNestedPreFling(float velocityX, float velocityY) {

return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);

}

复制代码

不进行处理,而是做为嵌套子视图继续分发;时机up事件,拦截时,嵌套子视图处理之前

public boolean onNestedFling(

@NonNull View target, float velocityX, float velocityY, boolean consumed) {

if (!consumed) {

dispatchNestedFling(0, velocityY, true);

fling((int) velocityY);

return true;

}

return false;

}

复制代码

如果接受到通知时,未处理,则进行处理;并做为嵌套子view继续通知处理;时机up事件,拦截时,嵌套子视图处理之后

4.6 滑动结束

public void onStopNestedScroll(@NonNull View target) {

onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);

}

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

mParentHelper.onStopNestedScroll(target, type);

stopNestedScroll(type);

}

public void stopNestedScroll(int type) {

mChildHelper.stopNestedScroll(type);

}

复制代码

由于还是嵌套子视图角色,还需要通知其处理的嵌套父容器结束;时机up、cancel事件时

4.7 嵌套子视图优先处理

android中,从容器的默认拦截机制来看,父容器优先拦截;但是嵌套时做了额外判断,

滑动事件拦截中是这样判断的

yDiff > mTouchSlop && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0)

复制代码

滑动的坐标轴为0,也就是既不是x轴、也不是y轴;这说明,它作为嵌套父容器时,没有嵌套子容器传递给它;

另外如果滑动已经被拦截处理,则不希望其它进行再次拦截;这时由于嵌套拦截体系已经提供了交互的方法,如果不这样处理,就会导致和默认的事件机制冲突;因此,如果有这种情况,那就把重写父容器,让其支持嵌套滑动吧

5 小结


写在最后

在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。

如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!

加入我们吧!群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

交互的方法,如果不这样处理,就会导致和默认的事件机制冲突;因此,如果有这种情况,那就把重写父容器,让其支持嵌套滑动吧

5 小结


写在最后

在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。

如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!

加入我们吧!群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值