在 AndroID 开发中,滑动冲突总是我们一个无法避免的话题。而对于解决方案却是众说纷纭。比如 RecyclerVIEw 嵌套 RecyclerVIEw,直接通过相关方法禁掉内部 RecyclerVIEw 的滑动;ScrollVIEw 嵌套 RecyclerVIEw 直接把 ScrollVIEw 替换为 nestedScrollVIEw 等等。
但我们今天要说的是在自定义 VIEw 中遇到滑动冲突时,我们又应该如何处理呢?当然,今天的话题需要 VIEw 的事件分发机制做理论前提。
1. 简单介绍 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 开发艺术探索:
1.同一个事件序列是指从手指接触屏幕(
ACTION_DOWN
)的那一刻起,到手指离开屏幕(ACTION_UP
)的那一刻结束,中间含不定数量的ACTION_MOVE
事件。
2.某个 VIEw 一旦决定拦截事件,那么这一个事件序列都只能由它处理,并且它的onIntercepttouchEvent
() 方法也不会再调用。换句话说,比如一个VIEwGroup
里面有数个子 VIEw,一旦ACTION_DOWN
事件从 Activity 传到这个 VIEwGroup 被其拦截,则后续的 MOVE 和 UP 等事件也不会传递到里面的子 VIEw 中。
3.如果一个VIEw
一旦开始处理事件,如果它不消耗ACTION_DOWN
事件,即 ontouchEvent()返回为 false,那么同一事件序列中的其他事件也不会再交给它处理,直接会调用其父 VIEw 的 ontouchEvent()。
4.如果 VIEw 不消耗除ACTION_DOWN
以外的其他事件,那么这个点击事件会消失,此时父元素的 ontouchEvent() 并不会被调用,并且当然 VIEw 可以持续收到后续的事件,最终这些消失的点击事件会传递给 Activity 处理。
5.VIEwGroup
默认不拦截事件,VIEw
没有onIntercepttouchEvent
() 方法,一旦有事件传递给它,则直接会调用ontouchEvent
(),并且起默认都会消耗掉事件。除非它是不可点击的(即 clickable 和 longClickable 均为 false)。VIEw 的longClickable
默认都为 false,而clickable
分情况,比如 button 默认为 true,TextVIEw 默认为 false。
6.VIEw
的enable
属性不会影响ontouchEvent
() 的默认返回值,哪怕一个 VIEw 是 disable 状态的,只要它的 clickable 或者 longClickable 有一个为 true,那么它的 ontouchEvent() 就会返回 true。
7.requestdisallowIntercepttouchEvent
() 可以在子元素中干预父元素的事件分发过程,但是无法干预 ACTION_DOWN 事件。
8.事件优先顺序:setontouchListener() => ontouchEvent() => onClickListener()
一不小心发现还是挺多的,当然这些都是结论。
2. 处理自定义 VIEw 中的滑动冲突对于大多数 AndroID 开发来说,处理滑动冲突好像很难,但实战一下又发现,好像也挺简单,因为这个实际上是有套路可循的。基本就两种方案:外部拦截法 && 内部拦截法。
2.1 外部拦截法所谓外部拦截法,顾名思义,就是直接在父容器中直接拦截掉我们的滑动事件,让其不能进入到子元素中,这似乎和我们 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 的时候结合实际场景也就可以得心应手了。
文章提到的问题你遇到过吗?是怎么解决的?
最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和pdf(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
上述【高清技术脑图】以及【配套的架构技术pdf】可以 加我wx:X1524478394 免费获取
总结当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
以上是内存溢出为你收集整理的教你用两招就完美解决 Android 滑动冲突!全部内容,希望文章能够帮你解决教你用两招就完美解决 Android 滑动冲突!所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)