1. 引言

BottomSheetBehavior能实现怎样的效果,一图胜千言。

如果仅仅是实现上下拖动和隐藏的功能。抛开BottomSheetBehavior自己实现也不难,在没有CoordinatorLayout的年代,这种效果往往是纯手工打造。既然如此为何Google要专门设计BottomSheetBehavior呢?为了搞清楚这个问题,我查阅源码探究了一番,确实发现了一些隐秘的角落。我将从以下几个方面讲解BottomSheetBehavior的设计思路

  1. 讲解BottomSheetBehavior的几种状态
  2. 讲解BottomSheetBehavior的事件分发
  3. 讲解BottomSheetBehavior如何处理嵌套滑动
  4. 实现高德地图首页效果,欢迎关注字节小站微信公众号号

2. BottomSheetBehavior的几种状态

BottomSheetBehavior一共有6种状态

  1. STATE_EXPANDED 全部展开状态
  2. STATE_COLLAPSED 收起状态
  3. STATE_DRAGGING 拖动状态
  4. STATE_SETTLING
  5. STATE_HIDDEN 隐藏状态
  6. STATE_HALF_EXPANDED 半展开状态

系统通过哪种方式实现每种状态不同的偏移量呢?

  1. layout阶段通过ViewCompat.offsetTopAndBottom(child, offset)实现偏移量
  2. 用户触摸交互阶段通过ViewDragHelper.dragTo(left,top,dx,dy)实现偏移量

2.1 Layout阶段

Layout阶段最后会通过findScrollingChild方法,寻找开启了嵌套滑动的后代View。其实这就是Google单独研发出BottomSheetBehavior的主要考量。满足支持嵌套滑动的BottomSheet效果。

2.2 用户触摸交互阶段


2.3 状态对应的偏移量

状态 偏移量
STATE_COLLAPSED collapsedOffset
STATE_EXPANDED getExpandedOffset()
STATE_HALF_EXPANDED halfExpandedOffset
STATE_HIDDEN parentHeight

1. 计算 collapsedOffset

变量名 默认值
PEEK_HEIGHT_AUTO 常量值-1
peekHeightMin 默认值64dp,用户不可修改
peekHeightAuto 默认值true,用户可设置
peekHeight 默认值0,如果设置为PEEK_HEIGHT_AUTO peekHeightAuto为true否则为false,如果设置小于-1则为0
fitToContents 默认值true,用户可设置
fitToContentOffset Math.max(0, parentHeight - child.getHeight())

peekHeight默认值为0。设置逻辑如下

  1. height为-1,则peekHeightAuto设置为true。
  2. 否则peekHeightAuto为false,而且peekHeight最小值为0。

计算collapsedOffset值有四种情况

Case peekHeightAuto fitToContents
case1 true true
case2 true false
case3 false true
case4 false false

返回值

Case 返回值
Case1 Math.max(parentHeight - Math.max(peekHeightMin, parentHeight - parentWidth * 9 / 16), fitToContentsOffset)
Case2 parentHeight-Math.max(peekHeightMin, parentHeight - parentWidth * 9 / 16)
Case3 Math.max(parentHeight - peekHeight, fitToContentsOffset)
Case4 parentHeight - peekHeight

2. 计算 halfExpandedOffset

3. 计算 expandedOffset

4.如何固定BottomSheetBehavior的高度?

了解这些值的计算有什么好处。假设我想让BottomSheetBehavior,固定高度,不能向上滑也不能向下滑。那我们则需要将collapsedOffset和expandedOffset设置为一样的值才行。

代码如下

为了良好的阅读体验没有使用代码块呈现代码,如果你想获取代码请访问github代码库

3. 讲解BottomSheetBehavior的事件分发

学习Android事件分发是有方法的。我总结为"三板斧"分析法

  1. 源码分析
  2. 场景化
  3. 树形图分析

3.1 三板斧之源码分析

从onInterceptTouchEvent的代码中,可以看到viewDragHelper.shouldInterceptTouchEvent(event),说明拦截方法会让ViewDragHelper方法处理。

ViewDragHelper的初始化,会传入ViewDragHelper.Callback dragCallback对象,该对象的boolean tryCaptureView(View child, int pointerId)方法决定viewDragHelper.shouldInterceptTouchEvent的返回值。

onInterceptTouchEvent的拦截逻辑如下

onTouchEvent主要交由ViewDragHelper#processTouchEvent处理,如果是Move事件最终会调用dragTo方法进行移动

3.2 三板斧之场景化和树形图分析

假设有场景如下,用户可以在HeadLayout、NestedScrollingChild,TopMostLayout区域内上下滑动。这三个case,事件的处理路径如何呢?

转化成树形图如下

设置BottomSheetBehavior为LinearLayout的Behavior

3.2.1. 在HeadLayout区域内上下滑动

  1. Down事件处理,初始状态,在ViewDragHelper的shouldInterceptTouchEvent方法中不会调用tryCaptureViewForDrag方法,该方法返回false。在BottomSheetBehavior onInterceptTouchEvent中完整事件路径如下,红线表示事件的分发路径

结合树形图分析。由于BottomSheetBehavior不拦截事件。Down事件分发流程如下

最终会调用到BottomSheetBehavior的onTouchEvent方法,会调用到ViewDragHelper的processTouchEvent方法

最终会将ViewDragHelper的dragState设置为STATE_DRAGGING

  1. MOVE事件在BottomSheetBehavior onInterceptTouchEvent分发流程如下

接下来直接调用 BottomSheetBehavior 的onTouchEvent方法。同样调用到ViewDragHelper的processTouchEvent方法

3.2.2. 在NestedScrollingView区域内上下滑动

1.Down事件分发到BottomSheetBehavior的onInterceptTouchEvent分发流程如下

由于不拦截。Down事件分发给NestedScrollingChild,NestedScrollingChild会启动嵌套滑动,与BottomSheetBehavior配合完成嵌套滑动

2.Move事件分发流程比较复杂,当在NSC中Move的距离没达到阈值时,MOVE会继续分发到BottomSheetBehavior的onInterceptTouchEvent中,当在NSC中MOVE距离达到阈值时,会调用parent.requestDisallowInterceptTouchEvent(true)从此直达NSC,就是纯粹的嵌套滑动了。

接下来事件交由NSC分发,当MOVE距离大于阈值时,事件直接交由NSC处理。

3.2.2. 在TopMostLayout区域内上下滑动,该区域与NSC区域没有交集

Down事件同上

MOVE事件,当距离>ViewDragHelper阈值时

由于MOVE事件拦截了,会交由BottomSheetBehavior onTouchEvent处理,如下图

4. 结束

至此已基本讲解完BottomSheetBehavior的事件分发机制。具体细节未能尽善尽美。纸上得来终觉浅,希望读者可以结合文章去探索源码。
下次我将用BottomSheetBehavior来实现高德地图首页效果。欢迎持续关注

我开源了一个方便RecyclerView吸顶的Android库,欢迎您访问https://github.com/lizijin/StickyHeaderForRecyclerView,如果您使用本库,请提出您的宝贵意见。

它目前支持以下功能:

  • 支持复杂吸顶View功能
  • 支持多类型吸顶功能
  • 支持开启和关闭吸顶功能
  • 支持设置吸顶偏移量
  • 支持兼容ItemDecoration和ItemAnimator
  • 支持RecyclerView数据变化和滚动到指定位置
  • 其它更多功能

如果你有任何问题,欢迎通过以下方式联系我

BottomSheetBehavior实现高德地图首页效果之原理篇相关推荐

  1. 仿高德地图首页效果,简单代码实现

    2021/11/10日高德的首页效果是这样的 然后我的效果 关键代码 behavior.addBottomSheetCallback(object : BottomSheetBehavior.Bott ...

  2. android仿高德地图首页,Android BottomSheet 的使用(仿高德地图的列表效果)

    最近项目中突然要实现高德地图中列表的效果,刚开始一筹莫展,以为是自定义控件还是通过手势进行判断 ,果断蒙了,百度谷歌了一下最后发现原来谷歌早就就出来了这样的效果--android.support.de ...

  3. android 高德地图动画,使用MotionLayout实现高德地图bottomSheets效果

    高德效果 搜到的一个效果,附上链接,用的behavior,我没下源码看,因为我只是想尝试另外一种方式.具体效果暂不知Android 仿高德地图可拉伸的BottomSheet 以下是我用motionla ...

  4. vue-cli3.0引入高德地图3d效果两种方法+实例+填坑

    前言: 因为项目需要引入高德地图的3d效果,找了很多资料,在这里记录下方法和实例组件 ***注意: 因为两个方法代码量都特别大,这里分2个页面详细说一下,方法一是链接出去专门说,最下面有更多资料,高德 ...

  5. 【高德地图WEB开发】 入门篇(地图/搜索/经纬度/缩放层级)

    前言 本篇文章是高德地图web开发入门篇,实现地图搜索等基本功能,后续会继续更新地图标记点.驾车.骑行.货车等单地点\多地点导航.公交路线.兴趣周边点.及其他地图功能开发文章,所有功能均使用js实现, ...

  6. 可用!三行代码高仿高德地图三段式抽屉效果

    三行代码 废话不在前面说,直接上代码! 将 XML 根布局设置为 CoordinatorLayout <android.support.design.widget.CoordinatorLayo ...

  7. marker 头像 高德地图_高德地图头像怎么更换 高德地图更换头像图文教程

    相信绝大部分人都知道微信头像以及QQ头像怎么更换,而设置头像也是很多人喜欢做的一件事情.而对于经常使用高德地图的用户来说,头像该怎么设置呢?对于这群用户,下面百事网小编为大家带来详细的高德地图更换头像 ...

  8. 深入理解最强桌面地图控件GMAP.NET ---高德地图

    前一篇介绍了GMAP.NET的理论基础,这篇介绍如何集成高德地图.高德地图自从为IOS6提供地图后,目前已经被大众所熟知了. 在我集成国内地图的经验来看,也是最符合墨卡托投影法的. 前面的相关文章链接 ...

  9. 全新界面改版+实用功能上线 高德地图新版全体验

    全国大部分地区已进入踏青季,为了让人们的出行更加便捷,国内领先的移动互联网地图软件高德地图推出了全新的7.6.6版本,从功能.体验.视觉三个方面都取得了突破性的改进.一目了然的是其UI界面有了很大的变 ...

最新文章

  1. 10 年 IT 老兵给新人程序员的几点建议
  2. 二进制包 mysql_二进制包;mysql
  3. research how javascript is generated by java class
  4. rh9.0虚拟机dmesg启动过程分析(1)
  5. 如何使用CloudFormation自动在AWS EC2上安装Anaconda
  6. 嵌入式linux下网卡设备驱动与网络协议栈的设计实现.pdf,嵌入式系统的通用网络驱动模型设计与实现.pdf...
  7. 【转载】面向对象建模与数据库建模两种分析设计方法的比较
  8. tornado web mysql_Tornado WEB服务器框架 Epoll-- 【Mysql数据库】
  9. LeetCode-14.最长公共前缀(字符串的erase()函数)
  10. linux安装yum报错Unable to locate package yum解决方案
  11. flv.js插件播放flv格式的视频实例(vue)
  12. 编译 nginx + http-flv 模块
  13. bash: yum:未找到命令
  14. Docker 基础 (一)
  15. win10任务栏显示“中/英“语言标识
  16. W: GPG error: http://dl.google.com/linux/chrome/deb stable Release: The following signatures couldn'
  17. DSP开发-C语言环境
  18. javaweb汽车租赁系统springboot+vue+nodejs
  19. vbs 杀死进程_VBS杀死指定进程
  20. java屏蔽虚拟按键代码_Android6.0 源码修改之屏蔽导航栏虚拟按键(Home和RecentAPP)/动态显示和隐藏NavigationBar...

热门文章

  1. 体验了杭州的最多跑一次,这是IT的胜利
  2. 求集合数据的均方差pta
  3. java线上问题定位
  4. 外食孕妈咪的营养计划
  5. acme云服务器生成证书_腾讯云DNSPOD,利用acme申请免费ssl证书。
  6. networkmanager connect Ap by bssid fail
  7. 坚持写博客4个月粉丝127——获粉感言
  8. 图解区块链跨链协议之“哈希时间锁”
  9. (学习java)水果超市管理系统
  10. Cadence添加工艺库 | 虚拟机与宿主机共享文件