Android 触摸事件的研究与应用
移动时代 Android app 开发必备技能

Android 触摸事件的研究与应用
背景
三年前(2016 年)由于公司项目中涉及到一个仿 iOS 系统列表控件列表项的侧滑菜单 UI 交互效果(这个 UI 交互效果竟然 iOS 系统自带,苦逼的 Android 程序员啊。。。),尽管当时有 Android 端现有的轮子可用(SwipeMenuListView
),但是出于以下两点原因我只能自造轮子:
- 当时 Android 项目列表控件已经全部迁移至
RecyclerView
,一个强大到不行的谷歌官方支持库用于替代系统 SDK 自带的复杂、难以扩展、老态龙钟的ListView
的地位,没有现有好用的RecyclerView
侧滑菜单轮子可用,也许当时根本就没有(当时并没有找到),2015 年谷歌发布了 Android Lolipop 棒棒糖 5.0 系统。 - 发自内心深处的好学特质。为了搞清楚 Android 触摸技术原理并能够根据项目需求得心应手进行实际应用,为了能够技术独立,不被牵制,随心所欲地创新(力所能及的范围内)。
尽管之前已经接触过一些简单的自定义 View 技术,但是为了更加有条理地进行下去,先后查阅了官方文档,google 了资料,学习借鉴了 SwipeMenuListView
的实现方式。
摸打滚爬了最终实现了 RecyclerView
侧滑菜单的雏形效果,但是还是存在已知的 bug,然后发生了一些外界因素,干扰了项目的正常运行,所以烂尾到现在的 2019 年。。。
之所以在 2019 年的最近重启了该项目,因为本人有个 2015 年写的个人 App 现在维护需要用到这个技术,也就是老生常谈烂大街的侧滑菜单交互,只不过这个是用在 App 首页那种侧滑显示菜单布局,当前已经实现的是 RecyclerView
里的 item 侧滑菜单,其实原理都是一样一样的,当时就考虑了这些,所以当时把 SwipeMenuRecyclerView
和自定义的侧滑布局 SwipeLayout
分离实现的。2013 年 App 侧滑交互已经开始流行了,类似 QQ 那种,后来又演变了各种侧滑效果的变种版本,包括后来的谷歌官方的 DrawerLayout
。 还是一句话:原理都是一样一样的。现在的流行趋势是:
- App 根本就是废弃了这种交互效果(我也不知道为什么,审美疲劳?感叹前端的风云变幻莫测!)
- 借鉴了 Google Android 原生系统桌面启动器的负一屏效果。现在的 QQ 设计的就是这个样子的(估计是借鉴的原生系统效果)
无独有偶,最近我把原先的自定义侧滑布局的各种 bug 解决了,完美地实现了负一屏侧滑布局效果,至少我人工测试了 N 遍。
为了乘热打铁,写了这边总结文章,下面开始进入严格的技术探讨篇(非技术人员可以略过)
技术
- #### 需求
程序是解决现实世界问题的抽象化模型和思维
- App 在用户手指触摸屏幕时水平滑动(侧滑手势),打开或者关闭二级内容(负一屏或者称之为导航视图)
- 实现
START(LEFT)
,END(RIGHT)
,TOP
,BOTTOM
,ABOVE
,BELOW
样式,多姿多彩,完美人生。
- #### 设计
- 类名
站在抽象化思维角度上看,侧滑布局需要满足各种姿势的侧滑需求,并且能够应用在多种场景下(作为列表控件 itemview 的侧滑菜单,整屏侧滑),因此我们给这个自定义的布局类起名为
SwipeLayout
(SwipeMenuLayout) - 继承
首选
ViewGroup
,而不是现有的布局容器(FrameLayout,LinearLayout。。。),因为这样可以更好地控制SwipeLayout
下的 child view 的布局。 - 依赖
-
GestureDetectorCompat
主要用它来监测 fling 手势。 Android SDK 和支持库中有大量提供手势方面的 public 接口,GestureDetectorCompat
类能够帮助你检测 Android 常见的标准手势,比如 单机(singleTap),双击(doubleTap),长按(longPress),滚动(scroll),滑翔(fling) 等等手势。当然了,可以完全不用GestureDetectorCompat
类,使用VelocityTracker
来跟踪滚动速度然后根据需要判断触发 fling 手势。 OverScroller
用于计算 child view 的滚动偏移量。 官方已经废弃支持库中的ScrollerCompat
,OverScroller
属于 SDK 中的类,顾名思义,OverScroller
拥有 over scroll(滑动超出正常的可滚动范围) 检测的能力,这样你就可以自己实现一些自定义 over scroll 的 ui 效果,比如类似 iOS 的粘性效果,或者 Android 系统默认的光晕效果。
-
- 类名
站在抽象化思维角度上看,侧滑布局需要满足各种姿势的侧滑需求,并且能够应用在多种场景下(作为列表控件 itemview 的侧滑菜单,整屏侧滑),因此我们给这个自定义的布局类起名为
- #### 开发
SwipeLayout
布局参数app : layout_gravity
用于控制次视图(负一屏或菜单栏)的定位和布局。 这个 flag 的可选值有START
,END
,TOP
,BOTTOM
,ABOVE
,BELOW
。 其中START
,END
,TOP
,BOTTOM
间互斥,ABOVE
,BELOW
间互斥。 有效 12 种组合为:- 水平方向:
START
,START | ABOVE
,START | BELOW
,END
,END | ABOVE
,END | BELOW
, - 垂直方向:
TOP
,TOP | ABOVE
,TOP | BELOW
,BOTTOM
,BOTTOM | ABOVE
,BOTTOM | BELOW
。
- 水平方向:
onMeasure
主要根据上级的ViewGroup
传递过来widthMeasureSpec
和heightMeasureSpec
测量 child view,通过app : layout_gravity
找到 content view 和 secondary view。这里主要注意在heightMeasureSpec
的 heightMode 不为EXACTLY
的时候需要记录 那些 child view 的高度为MATCH_PARENT
并设置一个MeasureSpec
的高度为当前遍历完所有的child view后得出的最大的高度和EXACTLY
mode去重新测量这些 child view 以满足其布局要求。onLayout