2024年Android最全Android NestedScrolling机制完全解析 带你玩转嵌套滑动,2024年最新2024大厂Android面试经验

最后

在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

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

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

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

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

int dxUnconsumed, int dyUnconsumed);

public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);

public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);

public boolean onNestedPreFling(View target, float velocityX, float velocityY);

public int getNestedScrollAxes();

在写具体的实现前,先对需要用到的上述方法做一下简单的介绍:

  • onStartNestedScroll该方法,一定要按照自己的需求返回true,该方法决定了当前控件是否能接收到其内部View(非并非是直接子View)滑动时的参数;假设你只涉及到纵向滑动,这里可以根据nestedScrollAxes这个参数,进行纵向判断。

  • onNestedPreScroll该方法的会传入内部View移动的dx,dy,如果你需要消耗一定的dx,dy,就通过最后一个参数consumed进行指定,例如我要消耗一半的dy,就可以写consumed[1]=dy/2

  • onNestedFling你可以捕获对内部View的fling事件,如果return true则表示拦截掉内部View的事件。

主要关注的就是这三个方法~

这里内部View表示不一定非要是直接子View,只要是内部View即可。

下面看一下我们具体的实现:

public class StickyNavLayout extends LinearLayout implements NestedScrollingParent

{

@Override

public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)

{

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

}

@Override

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

{

boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;

boolean showTop = dy < 0 && getScrollY() > 0 && !ViewCompat.canScrollVertically(target, -1);

if (hiddenTop || showTop)

{

scrollBy(0, dy);

consumed[1] = dy;

}

}

@Override

public boolean onNestedPreFling(View target, float velocityX, float velocityY)

{

if (getScrollY() >= mTopViewHeight) return false;

fling((int) velocityY);

return true;

}

}

  • onStartNestedScroll中,我们判断了如果是纵向返回true,这个一般是需要内部的View去传入的,你要是不确定,或者担心内部View编写的不规范,你可以直接return true;

  • onNestedPreScroll中,我们判断,如果是上滑且顶部控件未完全隐藏,则消耗掉dy,即consumed[1]=dy;如果是下滑且内部View已经无法继续下拉,则消耗掉dy,即consumed[1]=dy,消耗掉的意思,就是自己去执行scrollBy,实际上就是我们的StickNavLayout滑动。

  • 此外,这里还处理了fling,通过onNestedPreFling方法,这个可以根据自己需求定了,当顶部控件显示时,fling可以让顶部控件隐藏或者显示。

以上代码就能实现下面的效果:

对于fling方法,我们利用了OverScroll的fling的方法,对于边界检测,是重写了scrollTo方法:

public void fling(int velocityY)

{

mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);

invalidate();

}

@Override

public void scrollTo(int x, int y)

{

if (y < 0)

{

y = 0;

}

if (y > mTopViewHeight)

{

y = mTopViewHeight;

}

if (y != getScrollY())

{

super.scrollTo(x, y);

}

}

详细的解释可以看上面提到的博客,这里就不重复了。

到这里呢,可以看到NestedScrolling机制说白了非常简单:

就是NestedScrollingParent内部的View,在滑动到时候,会首先将dx、dy传入给NestedScrollingParent,NestedScrollingParent可以决定是否对其进行消耗,一般会根据需求消耗部分或者全部(不过这里并没有实际的约束,你可以随便写消耗多少,可能会对内部View造成一定的影响)。

用白话和原本的事件分发机制作对比就是这样的(针对正常流程下一次手势):

  • 事件分发是这样的:子View首先得到事件处理权,处理过程中,父View可以对其拦截,但是拦截了以后就无法再还给子View(本次手势内)。

  • NestedScrolling机制是这样的:内部View在滚动的时候,首先将dx,dy交给NestedScrollingParent,NestedScrollingParent可对其进行部分消耗,剩余的部分还给内部View。

具体的源码会比本博文复杂,因为涉及到触摸非内部View区域的一些交互,非本博文重点,可以参考源码。

四、原理


原理其实就是看内部View什么时候回调NestedScrollingParent各种方法的,直接定位到内部View的onTouchEvent:

@Override

public boolean onTouchEvent(MotionEvent e) {

switch (action) {

case MotionEvent.ACTION_DOWN: {

int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;

if (canScrollHorizontally) {

nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;

}

if (canScrollVertically) {

nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;

}

startNestedScroll(nestedScrollAxis);

} break;

case MotionEvent.ACTION_MOVE: {

if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {

dx -= mScrollConsumed[0];

dy -= mScrollConsumed[1];

vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);

}

} break;

case MotionEvent.ACTION_UP: {

fling((int) xvel, (int) yvel);

resetTouch();

} break;

case MotionEvent.ACTION_CANCEL: {

cancelTouch();

} break;

}

return true;

}

可以看到:

ACTION_DOWN调用了startNestedScroll;ACTION_MOVE中调用了dispatchNestedPreScroll;ACTION_UP可能会触发fling以调用resetTouch。

startNestedScroll内部实际上:

#NestedScrollingChildHelper

public boolean startNestedScroll(int axes) {

if (hasNestedScrollingParent()) {

// Already in progress

return true;

}

if (isNestedScrollingEnabled()) {

ViewParent p = mView.getParent();

View child = mView;

while (p != null) {

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

mNestedScrollingParent = p;

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

return true;

}

if (p instanceof View) {

child = (View) p;

}

p = p.getParent();

}

}

return false;

}

去寻找NestedScrollingParent,然后回调onStartNestedScroll和onNestedScrollAccepted。

dispatchNestedPreScroll中会回调onNestedPreScroll方法,内部的scrollByInternal中还会回调onNestedScroll方法。

fling中会回调onNestedPreFling和onNestedFling方法。

resetTouch中则会回调onStopNestedScroll。

代码其实没什么贴的,大家直接找到onTouchEvent一眼就能看到,调用的方法名都是dispatchNestedXXX方法,实际内部都是通过NestedScrollingChildHelper实现的。

所以如果你需要实现和NestedScrollingParent协作的内部View,记得实现NestedScrollingChild,然后内部借助NestedScrollingChildHelper这个辅助类,核心的方法都封装好了,你只需要在恰当的实际去传入参数调用方法即可。

ok,这样的一个机制一定要去试试,很多滑动相关的效果都可以借此实现;此外,后面可能会对CoordinatorLayout写一些博文~

结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

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

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

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

**(怎么能少了一份全面的面试题总结呢~)

[外链图片转存中…(img-rzmeShj9-1714822537029)]

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-L3cit2KW-1714822537030)]

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值