每日一问:Android 滑动冲突,你们都是怎样处理的
坚持原创日更,短平快的 Android 进阶系列,敬请直接在微信公众号搜索:nanchen,直接关注并设为星标,精彩不容错过。
在 Android 开发中,滑动冲突总是我们一个无法避免的话题。而对于解决方案却是众说纷纭。比如 RecyclerView
嵌套 RecyclerView
,直接通过相关方法禁掉内部 RecyclerView
的滑动;ScrollView
嵌套 RecyclerView
直接把 ScrollView
替换为 NestedScrollView
等等。但我们今天要说的是在自定义 View 中遇到滑动冲突时,我们又应该如何处理呢?
当然,今天的话题需要 View 的事件分发机制做理论前提,还不了解 View 的事件分发机制的小伙伴可以移步我之前面试系列的一篇文章:面试系列:讲讲 Android 的事件分发机制。
简单介绍 View 的事件分发机制
当然,这里也可以简单地提一下,基本的流程就是下面的伪代码。
public boolean dispatchTouchEvent(MotionEvent ev) {boolean consume = false;if (onInterceptTouchEvent(ev)) {consume = onTouchEvent(ev);}else{consume = child.dispatchTouchEvent(ev);}return consume;
}
当一个 ViewGroup
接收到一个事件的时候,首先会调用 dispatchTouchEvent()
方法进行事件分发,如果 onInterceptTouchEvent()
返回 true
,则代表当前 View 会拦截事件,则直接回调 onTouchEvent()
方法进行事件处理。如果不拦截,则直接回调子 View 的 dispatchTouchEvent()
方法,如此反复,一直到最里面的子 View。
当一个点击事件产生后,它的传递过程遵循以下顺序:Activity
=> Window
=> View
,即事件总是先传递给 Activity
,Activity
再传递给 Window
,最后 Window
再传递给顶层 DecorView
,然后遵循上面的方式一直在最里层 View
。
而处理事件则从最里层 View
不断回传给自己的外层 View
,如果一直没有 View
进行处理,则直接会回传到 Activity
中。
onTouchEvent()
返回true
代表自己要处理。
既然都提了这么一点,也就突然想给出一些结论,参考自 Android 开发艺术探索:
- 同一个事件序列是指从手指接触屏幕(ACTION_DOWN)的那一刻起,到手指离开屏幕(ACTION_UP)的那一刻结束,中间含不定数量的
ACTION_MOVE
事件。- 某个 View 一旦决定拦截事件,那么这一个事件序列都只能由它处理,并且它的
onInterceptTouchEvent()
方法也不会再调用。换句话说,比如一个 ViewGroup 里面有数个子 View,一旦 ACTION_DOWN 事件从 Activity 传到这个 ViewGroup 被其拦截,则后续的 MOVE 和 UP 等事件也不会传递到里面的子 View 中。- 如果一个 View 一旦开始处理事件,如果它不消耗 ACTION_DOWN 事件,即
onTouchEvent()
返回为 false,那么同一事件序列中的其他事件也不会再交给它处理,直接会调用其父 View 的onTouchEvent()
。- 如果 View 不消耗除 ACTION_DOWN 以外的其他事件,那么这个点击事件会消失,此时父元素的
onTouchEvent()
并不会被调用,并且当然 View 可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity
处理。- ViewGroup 默认不拦截事件,View 没有
onInterceptTouchEvent()
方法,一旦有事件传递给它,则直接会调用onTouchEvent()
,并且起默认都会消耗掉事件。除非它是不可点击的(即clickable
和longClickable
均为false
)。View 的longClickable
默认都为false
,而clickable
分情况,比如Button
默认为true
,TextView
默认为false
。- View 的
enable
属性不会影响onTouchEvent()
的默认返回值,哪怕一个View
是disable
状态的,只要它的clickable
或者longClickable
有一个为true
,那么它的onTouchEvent()
就会返回true
。requestDisallowInterceptTouchEvent()
可以在子元素中干预父元素的事件分发过程,但是无法干预 ACTION_DOWN 事件。- 事件优先顺序:
setOnTouchListener()
=>onTouchEvent()
=>onClickListener()
一不小心发现还是挺多的,当然这些都是结论,具体可以跟着 面试系列:讲讲 Android 的事件分发机制 进行源码流程探讨,你会发现上面的结论很容易得到。
处理自定义 View 中的滑动冲突
对于大多数 Android 开发来说,处理滑动冲突好像很难,但实战一下又发现,好像也挺简单,因为这个实际上是有套路可循的。基本就两种方案:外部拦截法 && 内部拦截法。
外部拦截法
所谓外部拦截法,顾名思义,就是直接在父容器中直接拦截掉我们的滑动事件,让其不能进入到子元素中,这似乎和我们 RecyclerView
嵌套 RecyclerView
时禁用内部 RecyclerView
滑动有那么一丝相似之处,就是内部不处理就完事儿了。但细细品来又完全不一样,这里的外部拦截法会让内部元素根本就收不到滑动事件。
这种方法明显非常适合我们上面讲的事件分发机制。我们在接收 ACTION_MOVE
事件的时候,直接通过使 onInterceptTouchEvent()
方法返回 true
来直接拦截掉事件就可以了,伪代码想必大家也知道了:
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {ev?.run { if (action == MotionEvent.ACTION_MOVE && 父容器需要点击事件){return true}}return super.onInterceptTouchEvent(ev)
}
代码很简单,我们仅仅需要在事件 ACTION_MOVE
时去处理我们的逻辑就好了,当满足我们的逻辑的时候,就拦截掉 ACTION_MOVE
事件给自己处理。
至于为什么不去拦截 ACTION_DOWN
和 ACTION_UP
,想必大家也清楚了。上面说了,如果拦截了 ACTION_DOWN
事件,那后续的 ACTION_MOVE
、ACTION_UP
等其它事件均不会在调用 onInterceptTouchEvent()
方法,会直接交给当前容器处理。而如果我们拦截掉 ACTION_UP
的话,肯定会导致子元素的点击事件无法被处理,因为大家肯定都知道一个点击事件从 ACTION_DOWN
开始,从 ACTION_UP
结束,二者缺一不可。
内部拦截法
内部拦截法相对外部拦截法会复杂一些,所以我们通常来说,都更加推荐用外部拦截法进行处理。不过,内部拦截法依然有着它非常重要的地位,具体情况有可能会遇到。
内部拦截法的话,需要 requestDisallowInterceptTouchEvent()
方法的支持,这个方法是干什么的呢?顾名思义,请求是否不允许拦截事件,其接收一个 boolean
参数,表示是否不允许拦截。
我们直接重写子元素的 dispatchTouchEvent()
方法,得到伪代码如下:
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {ev?.run { when(action){MotionEvent.ACTION_DOWN -> parent.requestDisallowInterceptTouchEvent(true)MotionEvent.ACTION_MOVE ->{if(满足需要让外部容器拦截事件){parent.requestDisallowInterceptTouchEvent(false)}}}}return super.dispatchTouchEvent(ev)
}
想必代码也是非常简单易懂的,我们给父容器的 requestDisallowInterceptTouchEvent()
传递的参数代表是否不允许其拦截事件,当参数为 true
的时候代表不允许拦截,为 false
的时候代表拦截。所以看起来和外部拦截法也就如出一辙了。
不过仅仅有这点修改还不够,我们通过前面的理论基础知道,当我们的父容器拦截掉 ACTION_DOWN
事件的时候,所有的事件都无法再传递到子元素中,自然也就不会调用上面我们写的 dispatchTouchEvent()
方法了。所以我们在内部拦截法的时候还需要重写父容器的 onInterceptTouchEvent()
方法。
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {ev?.run { if (action == MotionEvent.ACTION_DOWN){return false}}return super.onInterceptTouchEvent(ev)
}
至此,基本介绍了两种处理滑动冲突的解决方案,在自定义 View 的时候结合实际场景也就可以得心应手了。
除了滑动冲突,滑动处理也是一项非常有意思的工作,感兴趣的可以可以参考 NestedScrollingParent2 和 NestedScrollingChild2 哟。
文章参考自:《Android 开发艺术探索》
转载于:https://www.cnblogs.com/liushilin/p/11197376.html
每日一问:Android 滑动冲突,你们都是怎样处理的相关推荐
- Android滑动冲突解决方法
Android滑动冲突解决方法 滑动冲突 首先讲解一下什么是滑动冲突.当你需要在一个ScrollView中嵌套使用ListView或者RecyclerView的时候你会发现只有ScrollView能够 ...
- Android滑动冲突的完美解决方案
一.Android滑动冲突的完美解决方案 在Android开发中滑动冲突可以说是比较常见的一类问题,也是比较让人头疼的一类问题,两个原本完美的控件,组合在一起之后,忽然发现整个世界都不好了.滑动冲突主 ...
- Android滑动冲突解决方法(二)
之前的一遍学习笔记主要就Android滑动冲突中,在不同方向的滑动所造成冲突进行了了解,这种冲突很容易理解,当然也很容易解决.今天,就同方向的滑动所造成的冲突进行一下了解,这里就先以垂直方向的滑动冲突 ...
- 关于Android滑动冲突的解决方法(二)
之前的一遍学习笔记主要就Android滑动冲突中,在不同方向的滑动所造成冲突进行了了解,这种冲突很容易理解,当然也很容易解决.今天,就同方向的滑动所造成的冲突进行一下了解,这里就先以垂直方向的滑动冲突 ...
- android滑动冲突的解决方案
Android 中解决滑动的方案有2种:外部拦截法 和内部拦截法. 滑动冲突也存在2种场景: 横竖滑动冲突.同向滑动冲突. 所以我就写了4个例子来学习如何解决滑动冲突的,这四个例子分别为: 外部拦截法 ...
- Android滑动冲突解决方式(下拉刷新上拉加载更多,适配RecyclerView/ListView/ScrollView)
一.Android事件的分发机制 这里需要了解下Andorid事件的分发机制.事件分发一般是针对一组事件,即ACTION_DOWN > ACTION_UP 或 ACTION_DOWN > ...
- android 滑动冲突
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 通过move事件的 拦截. 在滑动组件中,重写 在拦截触摸事件的时候 这个方法, 然后 ...
- Android View的事件分发机制和滑动冲突解决方案
这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...
- 【Android 手势冲突】Colin带你彻底解决RecyclerView与ScrollView滑动冲突问题,并实现RecyclerView悬停导航栏(附demo哦)
在新一期的需求中,产品要求我们做出和美团某个页面类似的功能,即一个页面包含在scrollView中,上面一个部分放置一些常用的广告banner.宫格tab等,下面放置一个RecyclerView用于展 ...
最新文章
- 22个案例详解 Pandas 数据分析/预处理时的实用技巧,超简单
- 工业总线通信与OSI七层模型
- 基于VTKITK的Qt应用程序开发
- jquery form 序列化
- java 锁降级 知乎_HotSpot VM重量级锁降级机制的实现原理
- 关于 @ngrx/Store 下 obj 的扩展问题
- php 如何生成exe文件怎么打开,如何把PHP转成EXE文件
- 动图处理_100+动图带你看懂百大加工工艺(成型、表面处理、链接、切割)
- MySql的事务操作与演示样例
- 面向对象基础(继承类,抽象方法,接口,委托和事件)
- Java流(Stream)
- 数字逻辑速成复习备考期末
- oracle计算本年第几周,详细讲解“Oracle”数据库的“周数计算”
- 计算机网络(自顶向下方法)读书笔记----吐血整理
- IDEA编辑页面html jsp js java无法即时生效
- 微信小程序跳转微信小店
- Android安装App出现:“该文件包与具有同一名称的现有文件包存在冲突”的解决方法
- 8.4 Hyperplanes (超平面)
- C博客作业00--我的第一篇博客
- hadoop1.2.1+zookeeper3.4.6+hbase0.94集群环境搭建
热门文章
- 是否可以改变 宏的值_给女人的建议:当父母不同意你的男朋友,可以尝试六个方法...
- devc++鼠标变成了光标_Excel填充别再用鼠标拖拉了!用这4个方法,效率至少高10倍!...
- sublime快捷键代码对齐_Python配置sublime运行环境
- html5 css3炫酷效果,28种纯CSS3炫酷loading加载动画特效
- python中可迭代对象拆包时、怎么赋值给占位符_python3-数据结构和算法 » 1.2 解压可迭代对象赋值给多个变量...
- redis desktop manager连不上redis_Redis安装教程
- 使用php吧excel数据存到数据库,php如何存excel数据到数据库
- 计算机组成原理袁春风百度云,计算机组成原理 袁春风chap3homework.pdf
- pat 乙级 1019 数字黑洞(C++)
- RS-232转RS-485/422串口转换器产品介绍