事件分发机制 - 列表滑动过程中再次左滑右滑切换页面仿QQ联系人列表效果
作者:访客发布时间:2023-12-18分类:程序开发学习浏览:140
很多大厂 App 列表在滑动过程中,再次左滑或者右滑还是能切换页面,这点体验还是比较好的,因为不用等到页面停止再左滑右滑切换页面,体验很丝滑,比如QQ联系人那个ViewPager嵌套列表就支持。
写文章之前先聊一下其他的愉悦身心吧。最近总是在安卓群里或者B站里听到行情不好,找工作难,互联网没救了,寒冬来了,要饿死了这样的消极言论。其实大环境这样我们作为代码人也很难去改变,该学习还是得学习该吃饭睡觉还是得吃饭睡觉,该玩还是得玩。就如太阳东升西落、四时更替、风雨雷电我们都无法去改变,只能去适应,太阳升起就劳作太阳落下就休息,天要下雨就撑把伞,季节更替就添补衣裳,人法地,地法天,天法道,道法自然,人在自然面前何其渺小,只能去适应和顺乎自然,就业大环境也是如此没有什么不能去适应的,物竞天择适者生存,只要有了适应能力,就算互联网行业不复存在,我们凭借着适应能力,也能在其他领域很好的生存下来。上天有好生之德,大自然有这么多东西,想要饿死一个人还真是有点难,所以也不要过多的杞人忧天,该咋样还是得咋样,就业大环境自然会在该好的时候好起来,我们只要写好自己的代码,好好学习,通过代码拿到属于我们自己应有的就好了,不属于自己的一毫而莫取。
好了,就此打住,干正事了。我们想要列表在滑动过程中,再次左滑或者右滑能够切换界面,那肯定是要对事件分发机制有充分的理解。
产品经理: 做个这种效果不是很简单吗,先这样后那样在这样不就行了? Talk is cheap , Show me your code 。
再谈事件分发机制
什么是事件分发机制,用一句话来说,就是这个机制可以让 view 和 viewGroup 之间能够合理的处理的所到来的事件,你要处理我就让你处理,而不是事件来了你处理一下我处理一下毫无章法可言,整个view 体系都乱套了,反应到用户界面体验就是界面在胡乱的滑动。
为了能实现滑动过程中,再次左滑或者右滑能够切换界面(像QQ联系人列表那样),我们只看ViewGroup 的dispatchTouchEvent 方法就行了,无须涉及从手指触摸屏幕到一个具体view上这一整套流程,只要dispatchTouchEvent方法真正搞懂理解了,自然就能处理这个需求了。
先看dispatchTouchEvent 中关于onInterceptTouchEvent处理部分。
通过以上面代码我们可以了解到,onInterceptTouchEvent 只有在Down 事件或者mFirstTouchTarget不为null,并且在自身没有设置FLAG_DISALLOW_INTERCEPT这个标记才会调用。
为什么要这样搞个这种章法,有什么实际作用我们要怎么去理解?其实也很好理解,Down 事件就是先找到这一次事件流有哪些子View能处理,然后把它们用链表mFirstTouchTarget储存起来,链表插入方式是头插法,即下次事件都优先给最近处理此次事件的view来处理。
这里提出一个问题,如果FrameLayout 里有 View1 和 View2 ,View2在View1上层叠显示,两个View的onTouchEvent 处理 Down 事件都返回true其他返回默认 ,此时在View2上滑动mFirstTouchTarget 这个链表的结构是怎么样的?
好了,跳出问题接着继续分析这个代码设计 , 通过代码我们可以看到,处理事件冲突优先是选择内部拦截法,即通过改变 FLAG_DISALLOW_INTERCEPT标志位来处理,子view 和自己都可以改变这个标记位,当没有FLAG_DISALLOW_INTERCEPT这个标记位的时候,会调用自己的onInterceptTouchEvent方法,通过onInterceptTouchEvent 自己也可以决定给不给子view处理的机会。
这个机制说白点就是大家都平等,就看你要不要处理,如果你要处理我就给你处理(你给我设置了FLAG_DISALLOW_INTERCEPT),你处理了我就不处理了。如果你不处理,这时候我就自己替你决定要不要给你处理的机会(onInterceptTouchEvent )。
好了,理论讲完回归需求,ViewPager2( 左右滑动)中嵌套垂直滑动的RecyerView 怎么让RecyerView 在上下滑动过程中,再次左滑或右滑能让Vp2可以滑动?
我们自然要查看RecyerView 源码的。
阅读RecyerView 源码
RecyerView 为了自己能够处理事件肯定对onInterceptTouchEvent 动了手脚,我们来看看他的onInterceptTouchEvent方法做了什么伤天害理的事导致他爸爸和他儿子们都不能处理事件了。
果不其然,处理Down 事件时,自己在滚动状态就不让他爸爸vp2 处理了,所以我们改下代码就能实现需求了,如下。
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
val action = e.actionMasked
if (action == MotionEvent.ACTION_DOWN && scrollState == SCROLL_STATE_SETTLING) {
parent.requestDisallowInterceptTouchEvent(false)
stopScroll()
return scrollState == SCROLL_STATE_DRAGGING
}
return super.onInterceptTouchEvent(e)
}
就这里而言返回值直接默认scrollState == SCROLL_STATE_DRAGGING就行了,因为这里的返回值只是影响他孩子的事件处理当然也影响他爸爸的处理,我们这里返回ture和false 都一样,返回true的话性能更好一点,因为孩子没有重写事件消费方法也没有设置点击事件那些,就无需返回false 再去执行遍历询问子view 是否要处理事件了。
这些代码最重要的就是调用parent.requestDisallowInterceptTouchEvent(false)把这个事件的控制权重新交给他爸爸vp2处理。
- 程序开发学习排行
- 最近发表
-
- Wii官方美版游戏Redump全集!游戏下载索引
- 视觉链接预览最好的WordPress常用插件下载博客插件模块
- 预约日历最好的wordpress常用插件下载博客插件模块
- 测验制作人最好的WordPress常用插件下载博客插件模块
- PubNews Plus|WordPress主题博客主题下载
- 护肤品|wordpress主题博客主题下载
- 肯塔·西拉|wordpress主题博客主题下载
- 酷时间轴(水平和垂直时间轴)最好的wordpress常用插件下载博客插件模块
- 作者头像列表/阻止最好的wordPress常用插件下载博客插件模块
- Elementor Pro Forms最好的WordPress常用插件下载博客插件模块的自动完成字段