资讯专栏INFORMATION COLUMN

NestedScrolling(Android嵌套滑动机制)

mengbo / 1236人阅读

摘要:再次感谢在发布版本后,为了更好的用户体验,为的滑动机制提供了机制。如图在这之前,我们知道对事件分发是有自己的一套机制。主要是有三个函数。简单逻辑这样就可以实现嵌套滑动。三向父汇报滚动情况,包括子已消费和未消费的值。四结束整个嵌套滑动流程。

终于要在segmentfault写第一篇文章了,好鸡冻,这篇文章本来打算写在简书上的,但是由于页面不能富文本和markdown同时支持,看到Gemini大神的文章中酷炫、赏心悦目的效果后果断放弃简书,看文章本来就会枯燥,如果再没有美观的效果,那岂不是要边看边睡? 互联网给了我们这么多选择,那我肯定选择体验最棒的。感谢segmentfault

具体效果可以对比一下:

重点文字标记在segmentfault上支持code标签,简书上最多只能通过粗体实现。(反正我是没有找到更好的方法)

说到Gemini,我也是这两天因为了解NestedScrolling时接触到的,粗略看了一下资料和文章浏览数,赞! 我的大神!

好,前番就到这了,开始正题NestedScrolling

之前了解NestedScrolling的时候看过一些博客,其中就包括Gemini的segmentfault,当时看的时候因为不仔细不以为然,最后才发现这篇博客是对NestedScrolling介绍最清楚的,作为惩罚也好膜拜也罢,把本来可以cv过来的博客手动敲一遍,顺便补充一下自己的一些额外理解。

再次感谢Gemini


Android 在发布 Lillipop 版本后,为了更好的用户体验,Google为Android的滑动机制提供了NestedScrolling机制。

NestedScrolling的特性可以体现在哪儿呢?
比如你用了Toolbar,下面一个ScrollView,向上滚动隐藏Toolbar,向下滚动显示Toolbar,这里在逻辑上就是一个NestedScrolling ——因为你在滚动整个Toolbar在内的View的过程中,又嵌套滚动了里边的ScrollView

如图:

在这之前,我们知道Android对Touch事件分发是有自己的一套机制。主要是有三个函数:
dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent

这种分发机制有一个漏洞:

如果子view获得处理touch事件机会的时候,父view就再也没有机会处理此次touch事件,直到下一次手指触发。

也就是说,我们在滑动子view的时候,如果子view对这个滑动事件不需要处理的时候,只能抛弃这个touch事件,而不会传给父view去处理。

但Google新的NestedScrolling机制就很好的解决了这个问题。

NestedScrolling主要有四个类需要关注:

NestedScrollingChild
NestedScrollingChildHelper
NestedScrollingParent
NestedScrollingParentHelper

以上四个类都在support-v4包中提供,Lollipop中部分View默认实现了NestedScrollingChildNestedScrollingParent

v4包中NestedScrollView同时实现了NestedScrollingChild和NestedScrollingParent。

一般实现NestedScrollingChild就可以了,父View用support-design提供的实现了NestedScrollingParentCoordinatorLayout即可。

@Override
    public void setNestedScrollingEnabled(boolean enabled) {
        super.setNestedScrollingEnabled(enabled);
        mChildHelper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return mChildHelper.isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return mChildHelper.startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        mChildHelper.stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return mChildHelper.hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }

简单逻辑这样就可以实现嵌套滑动。

以上接口都是业务逻辑中自己调用,NestedScrollingChildHelper是如何实现的呢? 先看一下startNestedScroll方法

/**
     * Start a new nested scroll for this view.
     *
     * 

This is a delegate method. Call it from your {@link android.view.View View} subclass * method/{@link NestedScrollingChild} interface method with the same signature to implement * the standard policy.

* * @param axes Supported nested scroll axes. * See {@link NestedScrollingChild#startNestedScroll(int)}. * @return true if a cooperating parent view was found and nested scrolling started successfully */ 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交互的一些方法。
ViewParentCompat是一个和父View交互的兼容类,判断API version,如果在Lollipop上就调用View自带的方法,否则判断如果实现了NestedScrollingParent,则调用实现接口的方法。

子View与父View的交互流程如下:

一、startNestedScroll

首先子View需要开启整个流程(通过屏幕滑动触发touch事件),通过NestedScrollingChildHelper找到并通知实现了NestedScrollingParent的父View中onStartNestedScrollonNestedScrollAccepted方法。

二、dispatchNestedPreScroll

在子View的onIterceptTouchEventonTouch中(一般在MontionEvent.ACTION_MOVE事件里),调用该方法通知父View的滑动距离,该方法的第三第四个参数返回父View消费掉的scroll长度和子View的窗口偏移量,如果这个scroll没有被消费完,则子View处理剩余距离,由于窗口被移动,如果记录了手指最后的位置,需要根据第四个参数offsetInWindow计算偏移量,才能保证下一次touch事件的计算是正确的。

如果父View接受了滚动参数并部分消费,则该函数返回true,否则返回false。
该函数一般在子View处理Scroll前调用。

三、dispatchNestedScroll

向父View汇报滚动情况,包括子View已消费和未消费的值。
如果父View接受了滚动参数,部分消费则函数返回true,否则返回false。
该函数一般在子View处理Scroll后调用。

四、stopNestedScroll

结束整个嵌套滑动流程。

流程中NestedScrollingChildNestedScrollingParent对应如下:

NestedScrollingChildImpl NestedScrollingParentImpl
onStartNestedScroll onStartNestedScroll, onNestedScrollAccepted
dispatchNestedPreScroll onNestedPreScroll
dispatchNestedScroll onNestedScroll
stopNestedScroll onStopNestedScroll

一般是子View发起调用,父View接受回调。

需要关注dispatchNestedPreScroll中的consumed参数:

public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) ;

该参数是一个int类型的数组,长度为2,第一个元素是父View消费的x轴方向的滚动距离,第二个元素是父View消费的y轴方向的滚动距离,如果两个值均不为0,则表示父View已消费滚动距离,则需要对子View滚动距离进行修正,正因为有该参数,使得处理滚动事件时思路更加清晰,不会像以前一样被一堆滚动参数搞混。


自己理解的NestedScrolling简要流程图(不包含Fling事件及返回值的逻辑):


鸣谢:

Gemini大神segmentFault博客

crainzy CSDN博客

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/13408.html

相关文章

  • Android 嵌套滑动机制NestedScrolling

    摘要:效果如上图别嫌弃我在这之前,我们知道对事件的分发是有自己一套机制的。主要是有是三个函数和。该方法的第三第四个参数返回父消费掉的长度和子的窗体偏移量。三向父汇报滚动情况,包括子消费的部分和子没有消费的部分。 Android 在发布 Lollipop版本之后,为了更好的用户体验,Google为Android的滑动机制提供了NestedScrolling特性 NestedScrol...

    CoffeX 评论0 收藏0
  • 10分钟带你入门NestedScrolling机制

    摘要:在收到事件时,找到离自己最近的,与它进行绑定并关闭它的事件拦截机制。所以需要主动拦截这种事件,拦截标准超过的距离。当前触摸的不是支持机制的。这种机制将滑动事件的传递封装起来,通过辅助类实现和之间的连接和交互。 一、从一个简单的DEMO看什么是嵌套滚动 我们先来看一下DEMO的效果,直观的感受一下什么是嵌套滚动: showImg(https://segmentfault.com/img/...

    betacat 评论0 收藏0
  • 滑动 - 收藏集 - 掘金

    摘要:分析提到侧滑删除,一个经典的例子就是玩转仿探探卡片式滑动效果掘金讲起本篇博客的历史起源,估计有一段历史了。列表左右滑动开源组件掘金是一款用于为上的排布提供左滑右滑操作的库。 Android 仿 YouTube 拖拽视频效果的实现 - Android - 掘金Android仿YouTube拖拽视频效果的实现 youtube-like-drag-video-view 代码已经开源到GitH...

    ShevaKuilin 评论0 收藏0
  • 嵌套滚动机制

    摘要:在这之前,我们知道对事件的分发是有自己一套机制的。该方法的第三第四个参数返回父消费掉的长度和子的窗体偏移量。三向父汇报滚动情况,包括子消费的部分和子没有消费的部分。 谷歌在发布安卓 Lollipop版本之后,为了更好的用户体验,Google为Android的滑动机制提供了NestedScrolling特性 NestedScrolling的特性 比如你使用了Toolbar,下面一个Scr...

    Riddler 评论0 收藏0
  • Android列表,嵌套滑动

    摘要:那些酷炫的开源库整理整理开源库,文章提交,欢迎提交更新二维可拖动面板控件控件可用于房态盘计划表待办事项课程表等相关需要二维视图的场合,需要的可以收藏下五分钟带你看懂嵌套滑动机制在发布之后加入了嵌套滑动机制为嵌套滑动提供了更方便的处理方案。 仿淘宝、京东拖拽商品详情(可嵌套ViewPager、ListView、WebView、FragmentTabhost) 对于电商App,商品详情无疑...

    aboutU 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<