作者:手机用户2602936393 | 来源:互联网 | 2023-10-12 09:49
一、介绍
什么是分发机制?
首先了解下什么是分发机制,分发机制顾名思义,用来实现分配的功能,好比一个公司领导下发任务给部门领导,部门领导下发任务给员工。
View分发机制的作用
分发机制在view中起到什么作用呢,和上面举例类似,它的作用是用来分发view的点击事件,我们常遇到的如滑动冲突问题,就是典型的分发机制的一个难点,分发机制说起来难其实也不难,说简单也不简单,下面会一一介绍.
二、分发机制的传递规则
首先了解规则之前我们先了解几个关键对象
1.MotionEvent
这个应该都不陌生,点击事件的对象,从这个对象中可以获取到我的点击事件当前的状态,像MotionEvent.ACTION_UP、MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE,在事件分发中也是必不可少的一个对象.
2. 事件分发几个关键方法
①dispatchTouchEvent()–>分工
此方法作用在于分发任务,一个view执行分发之前首先会执行此方法,用来分发任务,我们可以理解为领导管理者,领导向下分发任务,起到一个管理分工作用.
②onInterceptTouchEvent()–>拦截
此方法作用在于拦截事件,当领导层也就是dispatchTouchEvent方法下发了分发任务,如果需要执行此任务就需要此方法告知我们的领导层(dispatchTouchEvent),我要接受这个任务,起到一个拦截任务的作用,如果不拦截则会由其他下级view处理是否拦截.
③onTouchEvent()–>处理
此方法作用在于处理事件,当我们拦截了分发的事件任务的时候,我们需要用到此方法来处理这次分发的任务,如果此方法不处理,则分发给下一级view去处理,例如我们接受了领导下发的任务,也成功拦截接受了任务,在处理的时候我们发现我们处理不了次任务或者不想处理,这时候就需要给其他员工去处理 (子view).
3.分发规则
想了解分发的传递规则首先我们一起看下这段伪代码,能更好理解.
public boolean dispatchTouchEvent(MotionEvent ev){
boolean cOnsume= false;
if(onInterceptTouchEvent(ev)){
cOnsume= onTouchEvent(ev);
}else{
cOnsume= child.dispatchTouchEvent(ev);
}
return consume;
}
由上代码可以很清楚的看出,分发的传递规则,首先dispatchTouchEvent归属于顶层view,默认consume返回false也就是不处理此次事件,但是我们看到onInterceptTouchEvent方法如果返回true,也就是说我们拦截了事件,这个时候会走此view的onTouchEvent方法,当onTouchEvent方法返回true的时候代表此次事件我们这个顶层view处理了,其他view无需处理此事件,如果onTouchEvent返回false代表我们处理不了或者不处理此次事件,则dispatchTouchEvent直接返回false,如果我们不拦截此次事件的话也就是onInterceptTouchEvent返回false,我们可以看到else中有这样的代码 child.dispatchTouchEvent(ev);,child顾名思义代表子view,也就是说我们onInterceptTouchEvent返回false(不拦截此次事件的话),我们交由下一个子view进行处理,子view也按照这样的逻辑依次传递下去.
分发顺序
一般分发顺序由Activity->Window->View(开始分发)->子view
上提到了分发规则,但是如果所有的view都不处理此次事件,那会如何呢?
如果所有view都不处理的话,事件会传递给Activity处理.
点击事件优先级
点击事件一般会走如下方法:
OnTouch(也就是OnTouchListener) > OnTouchEvent > OnClick.
由上可以看出点击事件的优先级, 当view处理点击事件时,首先会判断是否设置了OnTouchListener如果设置了那么OnTouch方法将被调用, 这时是否交由OnTouch方法还需要确认它的返回值, 如果返回false,则OnTouchEvent方法将被执行, 如果是true, OnTouchEvent不会被调用, 如果OnTouchEvent中,设置了OnClick则OnClick会被调用,也就是OnClick处理的优先级最低.
3.滑动冲突
提到分发机制必不可少的就是滑动冲突这类问题,也是分发机制涉及的主要普遍疑难问题之一.
要解决这类问题我们首先了解下事件序列, 从手势按下也就是DOWN,到MOVE,最后手势抬起UP,这期间也就是分发事件过程,称之为一个事件序列.
那我们解决滑动冲突也就是说在这个事件序列中解决事件分发问题,也就是一个事件中特定的情况下(比如我向上滑动) 将这个向上滑动的操作,分发给哪个View去处理,这也是解决滑动冲突的原理.
第一种也就是2个同方向View的滑动冲突, 这类问题比较复杂, 想要解决需要配合实际的业务场景, 因为是同方向我们无法通过角度,滑动方向等来判断,需要分发给哪个view执行,这次事件序列, 因此我们就需要一些附加条件来作为我们判断的条件,比如当内部view在什么状态的情况下我分发给它此次事件序列,当外部view在另一个状态下分发给外部此次事件序列.
第二种也就是横竖两个方向View的滑动冲突, 这类问题算是相对比较好解决,一般viewpager会自动处理这类问题,如果外部使用viewpager的小伙伴不需要考虑冲突的问题,如果其他情况,则就需要用到分发机制来处理.
① 外部拦截: 我们可以在外部view拦截,也就是使用拦截方法onInterceptTouchEvent,在此方法中判断我们的条件,可以根据滑动距离判断滑动方向以及角度,由此判断我们拦截方法是否返回true, 如果符合条件返回true,代表我们拦截了此方法,此时外部滑动生效, 如果返回false, 则不符合条件,交由内部view也就是子view处理。
② 内部拦截: 内部拦截也就是外部不处理任何事件,在内部进行同样的条件判断是否拦截,如果内部拦截成功,则内部滑动生效,如果拦截失败也就是返回false,则交由上级view处理, 这里是反向分发可以理解为 ,不符合我们的分发机制,交由上层view处理的话这里需要用到parent.requestDisalLowInterCeptTouchEvent(false), 此方法作用也就是子view调用如下方法时,父view会继续拦截本次事件序列.
滑动冲突举例就这两种了目前,其他情况下可更加更复杂的情况判断分发事件.
本文就到这里,如有错误请指正.