NestedScrollingChildHelper

这是一个用于实现子视图嵌套滚动的辅助类,并提供对Android 5.0之前版本的前兼容。

View要作为嵌套滚动中的Child,要在构造方法中实例化一个final的NestedScrollingChildHelper对象。该View中有一系列方法签名(即方法名和参数列表)和此类中相同的方法(这些方法实际来自于View实现的NestedScrollingChild接口),通过委托模式(delegate),这些方法的实际实现被委派给helper对象来完成。这提供了一种标准的结构化策略,来实现嵌套滚动。

例如,下面是NestedScrollView中相关的代码:

  1. public class NestedScrollView extends FrameLayout implements NestedScrollingParent,
  2. NestedScrollingChild {
  3. ......
  4. private final NestedScrollingParentHelper mParentHelper;
  5. // 这里文档中特别强调需要final修饰,而且要在构造方法中实例化
  6. private final NestedScrollingChildHelper mChildHelper;
  7. public NestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
  8. super(context, attrs, defStyleAttr);
  9. ......
  10. mParentHelper = new NestedScrollingParentHelper(this);
  11. mChildHelper = new NestedScrollingChildHelper(this);
  12. ......
  13. }
  14. // 下面9个方法来自于NestedScrollingChild接口,注意他们只是简单调用了mChildHelper中同名同参数列表的方法。
  15. @Override
  16. public void setNestedScrollingEnabled(boolean enabled) {
  17. mChildHelper.setNestedScrollingEnabled(enabled);
  18. }
  19. @Override
  20. public boolean isNestedScrollingEnabled() {
  21. return mChildHelper.isNestedScrollingEnabled();
  22. }
  23. @Override
  24. public boolean startNestedScroll(int axes) {
  25. return mChildHelper.startNestedScroll(axes);
  26. }
  27. @Override
  28. public void stopNestedScroll() {
  29. mChildHelper.stopNestedScroll();
  30. }
  31. @Override
  32. public boolean hasNestedScrollingParent() {
  33. return mChildHelper.hasNestedScrollingParent();
  34. }
  35. @Override
  36. public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
  37. int dyUnconsumed, int[] offsetInWindow) {
  38. return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
  39. offsetInWindow);
  40. }
  41. @Override
  42. public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
  43. return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
  44. }
  45. @Override
  46. public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
  47. return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
  48. }
  49. @Override
  50. public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
  51. return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
  52. }
  53. ......
  54. }

使用嵌套滚动功能的视图,应该始终使用ViewCompat, ViewGroupCompat 或者 ViewParentCompat中相关的兼容性静态方法。这保证了和Android 5.0之后的嵌套滚动视图之间互操作的正确性。

下面来具体的看这九个方法在NestedScrollingChildHelper类中的实现:

  1. 使能或禁止Child的嵌套滚动。

    1. public void setNestedScrollingEnabled(boolean enabled) {
    2. // 如果处于使能状态,有可能正在滚动,需要先停止。
    3. if (mIsNestedScrollingEnabled) {
    4. // 相当于调用View.stopNestedScroll()。
    5. // 这个方法如果View正在嵌套滚动,会停止嵌套滚动,反之无影响。
    6. // 注意这里实际还是会调用到NestedScrollingChildHelper.stopNestedScroll()。
    7. ViewCompat.stopNestedScroll(mView);
    8. }
    9. // 这个标志位代表能否嵌套滚动。
    10. mIsNestedScrollingEnabled = enabled;
    11. }
  2. 返回是否可以嵌套滚动。

    1. // 如果这里返回true,说明相应的子视图可以嵌套滚动。
    2. // 那么helper类会负责将滚动操作过程中的相关数据传递给相关联的nested scrolling parent。
    3. public boolean isNestedScrollingEnabled() {
    4. return mIsNestedScrollingEnabled;
    5. }
  3. 检查是否有关联的父视图,来接受嵌套滚动过程中的事件。

    1. // 注意这里的mNestedScrollingParent初始化时是null,
    2. // 是在下面的startNestedScroll(int axes)方法中找到的。
    3. public boolean hasNestedScrollingParent() {
    4. return mNestedScrollingParent != null;
    5. }
  4. 开始一个新的嵌套滚动。

    1. // 参数axes代表滚动轴,取ViewCompat#SCROLL_AXIS_HORIZONTAL and/or ViewCompat#SCROLL_AXIS_VERTICAL。
    2. // 如果找到了嵌套滚动的parent view,并且对于当前手势使能了嵌套滚动,返回true。
    3. public boolean startNestedScroll(int axes) {
    4. if (hasNestedScrollingParent()) {
    5. // 已经在嵌套滚动过程中。
    6. return true;
    7. }
    8. // 嵌套滚动功能使能情况下才能开始。
    9. if (isNestedScrollingEnabled()) {
    10. ViewParent p = mView.getParent();
    11. View child = mView;
    12. // 依次找父视图,直到找到第一个接受嵌套滚动的父视图。
    13. while (p != null) {
    14. // 实际调用的是NestedScrollingParent中的
    15. // onStartNestedScroll(View child, View target, int nestedScrollAxes)方法。
    16. // 返回父视图是否接受此嵌套滚动操作。
    17. if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
    18. mNestedScrollingParent = p;
    19. // 这里实际会调用NestedScrollingParentHelper.onNestedScrollAccepted(View child, View target, int axes)方法。
    20. ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
    21. return true;
    22. }
    23. if (p instanceof View) {
    24. child = (View) p;
    25. }
    26. p = p.getParent();
    27. }
    28. }
    29. return false;
    30. }
  5. 停止嵌套滚动

    1. public void stopNestedScroll() {
    2. if (mNestedScrollingParent != null) {
    3. // 实际调用的是NestedScrollingParentHelper.onStopNestedScroll(View target)。
    4. ViewParentCompat.onStopNestedScroll(mNestedScrollingParent, mView);
    5. mNestedScrollingParent = null;
    6. }
    7. }
  6. 将嵌套滚动过程中的一步分发给当前的nested scrolling parent。

    1. // 返回true代表该事件被分发(即父视图消费任意的嵌套滚动事件),反之则是无法被分发。
    2. public boolean dispatchNestedScroll(
    3. int dxConsumed, // 滚动中被此view消费的水平距离
    4. int dyConsumed, // 滚动中被此view消费的垂直距离
    5. int dxUnconsumed, // 滚动中未被此view消费的水平距离
    6. int dyUnconsumed, // 滚动中未被此view消费的垂直距离
    7. int[] offsetInWindow // 滚动前后此view的坐标偏移量
    8. ) {
    9. if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
    10. if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {
    11. int startX = 0;
    12. int startY = 0;
    13. if (offsetInWindow != null) {
    14. mView.getLocationInWindow(offsetInWindow);
    15. startX = offsetInWindow[0];
    16. startY = offsetInWindow[1];
    17. }
    18. // 此方法来自NestedScrollingParent.onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed);
    19. // 具体实现由父视图完成。
    20. ViewParentCompat.onNestedScroll(mNestedScrollingParent, mView, dxConsumed,
    21. dyConsumed, dxUnconsumed, dyUnconsumed);
    22. if (offsetInWindow != null) {
    23. mView.getLocationInWindow(offsetInWindow);
    24. // 偏移量是滚动后的位置减滚动前的位置
    25. offsetInWindow[0] -= startX;
    26. offsetInWindow[1] -= startY;
    27. }
    28. return true;
    29. } else if (offsetInWindow != null) {
    30. // No motion, no dispatch. Keep offsetInWindow up to date.
    31. offsetInWindow[0] = 0;
    32. offsetInWindow[1] = 0;
    33. }
    34. }
    35. return false;
    36. }

学习Android NestedScroll的更多相关文章

  1. 想学习Android开发

    最近被别人说知识面窄,心里受伤了.准备学学Android开发,如果能在手机里运行自己写的app,那是多么high ~~~ Android开发需要看什么资料呢? 说明:本人一直从事windows下的C+ ...

  2. 从零开始学习Android(一)Android环境的搭建

    好久没有开始写博客了,最近开始学习Android,所以想把学习的笔记都一一记录下来.一来是方便自己以后资料的查询,其次也是给Android新手朋友进行学习使用,再次也希 望得到高手的指点.废话少说,我 ...

  3. 《IT蓝豹》吹雪花demo,学习android传感器

    吹雪花demo,学习android传感器 吹雪花demo,学习android传感器,嘴巴对着手机底部吹一下就会出现飘着雪花效果. 算是学习android传感器效果.本例子主要是通过android.me ...

  4. 一起来学习Android自定义控件1

    概述 Android已经为我们提供了大量的View供我们使用,但是可能有时候这些组件不能满足我们的需求,这时候就需要自定义控件了.自定义控件对于初学者总是感觉是一种复杂的技术.因为里面涉及到的知识点会 ...

  5. 10个很棒的学习Android 开发的网站(转)

    看到江湖旅人 写的<10个很棒的学习iOS开发的网站 - 简书>,所以就忍不住写Android 啦,也希望对大家有帮助.我推荐的网站,都是我在学习Android 开发过程中发现的好网站,给 ...

  6. 学习android学习必备的java基础知识--四大内部类

    学习android必备的java基础知识--四大内部类 今天学习android课程,因为我的主专业是JAVA,但是兴趣班却有这其他专业的同学,学习android 需要具备一些java的基础知识,因此就 ...

  7. 深入学习Android有关网络连接的内容

    基于实际项目中碰到的些许问题,开始重新深入学习Android网络连接有关知识. Android的4.1.2版本的AndroidHttpClient模块setSoTimeout之后实际超时时间和设置的时 ...

  8. android学习——android架构

    android架构:在了解全局的情况下进行细致化的分析才能更有效的学习android的运行原理,才能更深刻的理解android开发: 1.架构图直观 2.架构详解 2.1.Linux Kernel 2 ...

  9. 跟Google学习Android开发-起始篇-构建你的第一个应用程序(4)

    说明:此系列教程翻译自Google Android开发者官网的Training教程,利用Chome浏览器的自动翻译功能作初译,然后在一些语句不顺或容易造成误解的地方作局部修正.方便英文不好的开发者查看 ...

随机推荐

  1. Ubuntu 安装之后的配置博文总结

    由于频繁地在各种机器上给别人安装ubuntu,每次安装之后都需要进行一些配置,现在以ubuntu12.04为例,就他人的一些配置博文总结如下: 1. Ubuntu安装中文输入法 http://www. ...

  2. canvas 绘制矩形

    XXX(x,y,width,height)   x矩形左上角x坐标                                   y矩形左上角y坐标                       ...

  3. json格式初涉

    json用{}表示json对象,[]表示数组,里面以值对的方式来存储信息 var jsondata='{"staff": [{"name":"jim& ...

  4. $(this).val()与this.value的区别?text()与html()的区别?

    $(this).val()与this.value 作用:都是获得当前Dom对象的value值(一般是表单元素) text radio checkbox select 基本没有什么区别,只是: this ...

  5. Linux系统挂载点与分区的关系(转载)

    计算机中存放信息的主要的存储设备就是硬盘,但是硬盘不能直接使用,必须对硬盘进行分割,分割成的一块一块的硬盘区域就是磁盘分区.在传统的磁盘管理中,将一个硬盘分为两大类分区:主分区和扩展分区.主分区是能够 ...

  6. jquery keyup 在IOS设备上输入中文时不触发

    今天做一个异步查询功能的时候发现在IOS设备上查询中文时keyup没有触发,在其他设备上时可以的,后来在stackoverflow上找到下面这种解决方法,贴出来算是抛砖引玉了. $h_input.on ...

  7. Git的思想和基本工作原理

    Git的思想和基本工作原理 Chapter: 开始了解Git 1. 先谈谈版本控制的一些事 2. Git诞生背后的一些故事 3. 版本控制:集中式VS分布式 4. Git的思想和基本工作原理 5. G ...

  8. 当DOCKER遇上ESXI

    特别是你要为DOCKER窗口设置静态IP,且和公司局域网打成一片的时候, 苦逼的测试就会开始,我差不多前前后后测试了四五天,一百多个容器报废. NETNS,NSENTER,PIPWORK,各种镜像合下 ...

  9. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAApcAAAB3CAIAAADZ1fxtAAAbFElEQVR4nO2dzbHDOo6FHY9S0WqqZt

  10. php PDO操作乱码问题

    前阶段用php写了一个小网页(每周一练),然后就一直忙着其他事也没管它,今天想着给它写个添加数据的页面,用pdo操作,没想到插入数据库的中文数据竟然乱码了,竟然乱码了!然后我就方了,赶紧检测数据传输过 ...