安卓事件分发机制深入浅出

首先,从最简单的一种情况开始

***************************************

分割线   demo1

***************************************

假设一个视图,外面一层是RelativeLayout,内部包了一个TextView,则事件传递的顺序如下

07-25 05:26:27.037 10515-10515/com.example.androidtest I/EventTest: MainActivity  0  Event:按下.............................................................
07-25 05:26:27.037 10515-10515/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 05:26:27.037 10515-10515/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 05:26:27.037 10515-10515/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onIntereptTouchcEvent  
07-25 05:26:27.037 10515-10515/com.example.androidtest I/EventTest: CustomTextView 3    dispatchTouchEvent  
07-25 05:26:27.037 10515-10515/com.example.androidtest I/EventTest: CustomTextView 3    onTouchEvent  
07-25 05:26:27.037 10515-10515/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onTouchEvent  
07-25 05:26:27.037 10515-10515/com.example.androidtest I/EventTest: MainActivity  0    onTouchEvent  
07-25 05:26:27.092 10515-10515/com.example.androidtest I/EventTest: MainActivity  0  Event:抬起.............................................................
07-25 05:26:27.092 10515-10515/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 05:26:27.092 10515-10515/com.example.androidtest I/EventTest: MainActivity  0    onTouchEvent  


事件传送 level 0 1 3  --  3 1 0

从log信息我们可以看出,首先Activity 接收到按下事件,然后activity把这个事件向下分别分发,结果一直到最后的TextView都没有人处理这个事件,

则这时系统只好又依次调用父级节点的onTouchEvent,仍然没有谁来处理,这个时候系统觉得,既然按下都没有谁来处理,抬起事件自然就更没有谁来处理了。

所以抬起事件索性就没有分发给View层了。呵呵,是不是和生活中的某种场景很像。


***************************************

分割线   demo2

***************************************

现在我们把上面的条件稍微修改下,把CustomTextView的dispatchTouchEvent返回值改为Ture

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.i(Constants.tag,tag+level+"    dispatchTouchEvent  ");
    boolean superReturn  =  super.dispatchTouchEvent(event);
    return true;
}
 
07-25 05:37:12.157 11242-11242/com.example.androidtest I/EventTest: MainActivity  0  Event:按下.............................................................
07-25 05:37:12.157 11242-11242/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 05:37:12.157 11242-11242/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 05:37:12.157 11242-11242/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onIntereptTouchcEvent  
07-25 05:37:12.157 11242-11242/com.example.androidtest I/EventTest: CustomTextView 3    dispatchTouchEvent  
07-25 05:37:12.157 11242-11242/com.example.androidtest I/EventTest: CustomTextView 3    onTouchEvent  
07-25 05:37:12.220 11242-11242/com.example.androidtest I/EventTest: MainActivity  0  Event:抬起.............................................................
07-25 05:37:12.220 11242-11242/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 05:37:12.220 11242-11242/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 05:37:12.220 11242-11242/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onIntereptTouchcEvent  
07-25 05:37:12.220 11242-11242/com.example.androidtest I/EventTest: CustomTextView 3    dispatchTouchEvent  
07-25 05:37:12.220 11242-11242/com.example.androidtest I/EventTest: CustomTextView 3    onTouchEvent  
 
这次TextView获取到了抬起事件。
并且我们可以看到,一旦触摸事件被消费掉了,则事件传递就会终止,并没有像第一个案例那样没有人消费的情况下又回传。
事件传送level 0 1 3  
 

***************************************

分割线  demo3

***************************************

下面,我们来看看onInterceptTouchEvent的作用
把RelativeLayout 的onInterceptTouchEvent修改如下
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.i(Constants.tag,tag+level+"    onIntereptTouchcEvent  ");
    boolean superReturn  =  super.onInterceptTouchEvent(ev);
    return true;
}
07-25 05:51:14.717 11917-11917/com.example.androidtest I/EventTest: MainActivity  0  Event:按下.............................................................
07-25 05:51:14.717 11917-11917/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 05:51:14.718 11917-11917/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 05:51:14.718 11917-11917/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onIntereptTouchcEvent  
07-25 05:51:14.718 11917-11917/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onTouchEvent  
07-25 05:51:14.718 11917-11917/com.example.androidtest I/EventTest: MainActivity  0    onTouchEvent  
07-25 05:51:14.805 11917-11917/com.example.androidtest I/EventTest: MainActivity  0  Event:抬起.............................................................
07-25 05:51:14.805 11917-11917/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 05:51:14.805 11917-11917/com.example.androidtest I/EventTest: MainActivity  0    onTouchEvent  
可以看到,事件没能传递到TextView层,这个和TextView本身的方法返回值无关,因为在外层ViewGroup就已经截断了事件传递
当然,这里我们只是阻止了事件向下传递,但并没有去handle这事件,因此这个事件又返回去了(调用外层onTouchEvent方法),
导致activity认为没人处理这个事件,所以抬起事件没有向下传递。

***************************************

分割线  demo4

***************************************

ok,继续,我们继续修改demo3代码,
修改了RelativeLayout的代码如下
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.i(Constants.tag,tag+level+"    dispatchTouchEvent  ");
    boolean superReturn  =  super.dispatchTouchEvent(ev);
    return true;
}

07-25 05:58:11.484 12111-12111/com.example.androidtest I/EventTest: MainActivity  0  Event:按下.............................................................
07-25 05:58:11.484 12111-12111/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 05:58:11.484 12111-12111/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 05:58:11.484 12111-12111/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onIntereptTouchcEvent  
07-25 05:58:11.484 12111-12111/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onTouchEvent  
07-25 05:58:11.573 12111-12111/com.example.androidtest I/EventTest: MainActivity  0  Event:抬起.............................................................
07-25 05:58:11.573 12111-12111/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 05:58:11.573 12111-12111/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 05:58:11.573 12111-12111/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onTouchEvent  
可以看到,后续的事件RelativeLayout也能接收到了。

***************************************

分割线  demo5

***************************************

OK,现在demo1变种,假设在事件传递过程中,在外层的ViewGroup的dispatchTouchEvent返回值改为True,会起到
onInterceptTouchEvent的效果吗,答案是:不会。
TextView 的分发方法
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.i(Constants.tag,tag+level+"    dispatchTouchEvent  ");
    boolean superReturn  =  super.dispatchTouchEvent(event);
    return true;
}
外层RelativeLayout的分发方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.i(Constants.tag,tag+level+"    dispatchTouchEvent  ");
    boolean superReturn  =  super.dispatchTouchEvent(ev);
    return true;
}
同时给予TextView
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.i(Constants.tag,tag+level+"    dispatchTouchEvent  ");
    boolean superReturn  =  super.dispatchTouchEvent(event);
    return true;
}

7-25 06:08:14.391 12901-12901/com.example.androidtest I/EventTest: MainActivity  0  Event:按下.............................................................
07-25 06:08:14.392 12901-12901/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 06:08:14.392 12901-12901/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 06:08:14.392 12901-12901/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onIntereptTouchcEvent  
07-25 06:08:14.392 12901-12901/com.example.androidtest I/EventTest: CustomTextView 3    dispatchTouchEvent  
07-25 06:08:14.392 12901-12901/com.example.androidtest I/EventTest: CustomTextView 3    onTouchEvent  
07-25 06:08:14.469 12901-12901/com.example.androidtest I/EventTest: MainActivity  0  Event:抬起.............................................................
07-25 06:08:14.469 12901-12901/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 06:08:14.470 12901-12901/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 06:08:14.470 12901-12901/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onIntereptTouchcEvent  
07-25 06:08:14.470 12901-12901/com.example.androidtest I/EventTest: CustomTextView 3    dispatchTouchEvent  
07-25 06:08:14.470 12901-12901/com.example.androidtest I/EventTest: CustomTextView 3    onTouchEvent  
可以看出,最终还是TextView获取了处理事件的权限。
也就是说,作为父节点的View,如果你想比子View优先处理事件,那么你就要通过onInterceptTouchEvent的返回值来控制。
通过dispatchTouchEvent方法的返回值来控制是无效的。

***************************************

分割线  demo6

***************************************

仍然是第一个例子的变种,

假设一个事件序列向下传递,如果到最终的View没有去处理,在事件返回的过程中,我可以获取处理View的权限吗,答案是可以

我们通过第一个例子知道,在事件返回的过程中,一直调用的是onTouchEvent方法,

我们改写RelativeLayout的onTouchEvent方法

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i(Constants.tag,tag+level+"    onTouchEvent  ");
    boolean superReturn  =  super.onTouchEvent(event);
    return true;
}

07-25 06:15:24.820 13160-13160/com.example.androidtest I/EventTest: MainActivity  0  Event:按下.............................................................
07-25 06:15:24.821 13160-13160/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 06:15:24.821 13160-13160/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 06:15:24.821 13160-13160/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onIntereptTouchcEvent  
07-25 06:15:24.821 13160-13160/com.example.androidtest I/EventTest: CustomTextView 3    dispatchTouchEvent  
07-25 06:15:24.821 13160-13160/com.example.androidtest I/EventTest: CustomTextView 3    onTouchEvent  
07-25 06:15:24.821 13160-13160/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onTouchEvent  
07-25 06:15:24.884 13160-13160/com.example.androidtest I/EventTest: MainActivity  0  Event:抬起.............................................................
07-25 06:15:24.884 13160-13160/com.example.androidtest I/EventTest: MainActivity  0    dispatchTouchEvent  
07-25 06:15:24.884 13160-13160/com.example.androidtest I/EventTest: CustomRelativeLayout  1    dispatchTouchEvent  
07-25 06:15:24.884 13160-13160/com.example.androidtest I/EventTest: CustomRelativeLayout  1    onTouchEvent  


可以看到,在事件最终传递到TextView之后,没有人拦截,也没有人处理,那么,在事件回传的过程中,RelativeLayout的onTouchEvent方法决定去处理

这个事件了。

然后,当抬起事件来临的时候,这个事件就给了RelativeLayout,没有再向下传给TextView.

也就是说按下的时候,这个事件已经找准了要处理自己的View,那就是RelativeLayout,那么,当抬起的时候,自然没必要把事件给别人了,而是传给了RelativeLayout就好了。







 
 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 事件分发机制是指在用户与Android设备进行交互时,Android系统如何接收并分发这些事件的过程。事件分发机制包括三个阶段:分发、拦截和处理。 1. 分发阶段:事件Android设备的底层硬件驱动程序开始,通过InputEvent分发给View层。在View层中,事件分为两类:MotionEvent和KeyEvent。MotionEvent表示触摸事件,包括按下、移动、抬起等操作;KeyEvent表示按键事件,包括按下和抬起。 2. 拦截阶段:在事件分发到View层后,会从最上层的View开始进行事件分发,直到有View对事件进行拦截。如果有View对事件进行了拦截,则事件不会继续向下分发,而是由该View进行处理。View是否拦截事件的判断由onInterceptTouchEvent方法完成,如果该方法返回true则表示拦截事件。 3. 处理阶段:如果事件没有被拦截,则会被传递到最底层的View进行处理。在View中,事件处理由onTouchEvent方法完成。如果该方法返回true,则表示事件已经被处理,不再需要继续向下分发;如果返回false,则会继续向上分发直到有View对事件进行拦截。 Android事件分发机制的流程如下: ![image.png](attachment:image.png) 需要注意的是,事件分发机制是一个逆向分发的过程,即从底层向上分发,而不是从顶层向下分发。这是因为底层的View需要先处理事件,如果底层的View不拦截事件事件才能向上分发

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值