Android事件分发总结

Android事件分发分两个方面:

1.View的事件分发(例如button这种控件)

2.ViewGroup的事件分发(我们平时项目里经常用到的各种布局,全都属于ViewGroup的子类)


先做简单分析,然后穷举栗子(对,你没看错,基本是穷举了),看完你就懂了,看不懂一、二部分分析的,直接看第三部分例子,找到规律,其实事件分发只要知道这个规律就行了,并不需要知道里面具体源码的流程,看源码只是加深理解。


一.View的事件分发


例如button注册了onClick和onTouch

button.setOnClickListener(new OnClickListener() {  
    @Override  
    public void onClick(View v) {  
        Log.d("TAG", "onClick execute");  
    }  
});  

button.setOnTouchListener(new OnTouchListener() {  
    @Override  
    public boolean onTouch(View v, MotionEvent event) {  
        Log.d("TAG", "onTouch execute, action " + event.getAction());  
        return false;  
    }  
});

首先会调用dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event) {  
    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch(this, event)) {  
        return true;  
    }  
    return onTouchEvent(event);  
} 
首先判断mOnTouchListener是否为空,mViewFlags & ENABLED_MASK判断当前点击的控件是否是enable的,按钮默认都是enable
然后就是调用mOnTouchListener的onTouch(),如果onTouch返回true,则直接返回true,如果返回false,则调用onTouchEvent(event)
在onTouchEvent(event)中会调用performClick(),如果注册了clicklistener则调用其onClick,并返回一个true,否则返回false
这样在dispatchTouchEvent返回了true,所以会传递给下一个action。注意:只有前一个action返回true,才会触发后一个action(move,up)


二.ViewGroup的事件传递机制

linearlayout,relativelayout继承自viewgroup,viewgroup继承自view(比view增加了可以添加子view的功能)

public class MyLayout extends LinearLayout {  
  
    public MyLayout(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
  
}


MyLayout作为父布局,里面放一个按钮

myLayout.setOnTouchListener(new OnTouchListener() {  
    @Override  
    public boolean onTouch(View v, MotionEvent event) {  
        Log.d("TAG", "myLayout on touch");  
        return false;  
    }  
});  
button1.setOnClickListener(new OnClickListener() {  
    @Override  
    public void onClick(View v) {  
        Log.d("TAG", "You clicked button1");  
    }  
}); 


效果:点击button,只会有click事件,myLayout的touch事件不会执行,只有点击空白区域,myLayout的touch才会执行

1.onInterceptTouchEvent,默认返回false,不拦截事件,会在dispatchTouchEvent被调用
public boolean onInterceptTouchEvent(MotionEvent ev) {  
    return false;  


2.Android中touch事件的传递,是先传递到ViewGroup,再传递到View的
当你点击了某个控件,首先会去调用该控件所在布局的dispatchTouchEvent方法,
然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。
如果我们点击了MyLayout中的按钮,会先去调用MyLayout的dispatchTouchEvent方法,可是你会发现MyLayout中并没有这个方法。
那就再到它的父类LinearLayout中找一找,发现也没有这个方法。那只好继续再找LinearLayout的父类ViewGroup,
你终于在ViewGroup中看到了这个方法,按钮的dispatchTouchEvent方法就是在这里调用的





3.在布局的dispatchTouchEvent中
首先会判断onInterceptTouchEvent是否拦截了事件,如果没有,则遍历子控件,并判断点击在那个子控件上,并调用子控件的child.dispatchTouchEvent(如此就进入子view 的dispatchTouchEvent中)
我们也因此证实了,按钮点击事件的处理确实就是在这里进行的。
调用子View的dispatchTouchEvent后是有返回值的。如果一个控件是可点击的,那么点击该控件时,
dispatchTouchEvent的返回值必定是true(这样就导致后面的代码无法执行到了),就会把MyLayout的touch事件拦截掉。
那如果我们点击的不是按钮,而是空白区域呢?这种情况就一定不会在第31行返回true了,而是会继续执行后面的代码
执行super.dispatchTouchEvent(ev)。这句代码会调用到哪里呢?当然是View中的dispatchTouchEvent方法了。之后的处理逻辑又和前面所说的是一样的了,也因此MyLayout中注册的onTouch方法会得到执行。





4.简单梳理一下

Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。
在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。
子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。


(1)这一系列的传递流程都是dispatchTouchEvent()方法来控制的,如果不人为地干预,事件将由上自下依次传递(因为默认是返回false不会拦截的),
传递到最底层的View,就由它的onTouchEvent()方法来处理事件,若处理成功返回true,若处理失败返回false,事件依次向上传递,
每个View都调用自己的onTouchEvent()方法来处理事件,若处理成功就终止传递,若处理失败就继续向上传递。
(2)经过人为的干预,若在向下传递的过程中被拦截了,即onInterceptTouchEvent()方法返回true,则事件将停止向下传递,
直接由当前的onTouchEvent()方法来处理,若处理成功则OK,若处理不成功,则事件会向上传递。
(3)另外,dispatchTouchEvent()方法中还有“记忆”的功能,如果第一次事件向下传递到某View,
它把事件继续传递交给它的子View,它会记录该事件是否被它下面的View给处理成功了,(怎么能知道呢?
如果该事件会再次被向上传递到我这里来由我的onTouchEvent()来处理,那就说明下面的View都没能成功处理该事件);
当第二次事件向下传递到该View,该View的dispatchTouchEvent()方法机会判断,若上次的事件由下面的view成功处理了,
那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功,那么这次的事件就不会向下传递了,
该View直接调用自己的onTouchEvent()方法来处理该事件。
(4)“记忆”功能的信息只在一系列事件完成之前有效,如从ACTION_DOWN事件开始,直到后续事件ACTION_MOVE,ACTION_UP结束后,
“记忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了(onTouchEvent()返回false),
那么后续的ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父View自己来处理。在下一次发生ACTION_DOWN事件的时候,还是会传递到该View的。



a.触摸控件(View)首先执行dispatchTouchEvent方法。


b.在dispatchTouchEvent方法中先执行onTouch方法,后执行onClick方法(onClick方法在onTouchEvent中执行,下面会分析)。


c.如果控件(View)的onTouch返回false或者mOnTouchListener为null(控件没有设置setOnTouchListener方法)或者控件不是enable的情况下会调运onTouchEvent,dispatchTouchEvent返回值与onTouchEvent返回一样。


d.如果控件不是enable的设置了onTouch方法也不会执行,只能通过重写控件的onTouchEvent方法处理(上面已经处理分析了),dispatchTouchEvent返回值与onTouchEvent返回一样。


e.如果控件(View)是enable且onTouch返回true情况下,dispatchTouchEvent直接返回true,不会调用onTouchEvent方法。


f.当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发下一个action(也就是说

dispatchTouchEvent返回true才会进行下一次action派发)。


三.例子分析


条件1:在activity中放入一个linearlayout

linearlayout设置了onTouchListener,onTouchListener.ouTouch的返回值在条件中具体设置


在上述条件下,分如下情况:

情况A:
linearlayout设置了onClickListerner,在onTouchListener.ouTouch返回true
点击空白出:
03-24 14:12:10.984 . activity dispatchTouchEvent
03-24 14:12:10.984 . linearLayout dispatchTouchEvent: action_down
03-24 14:12:10.984 . linearLayout onInterceptTouchEvent: action_down
03-24 14:12:10.984 . linearLayout onTouchListener.onTouch execut event: action_down
03-24 14:12:11.013 . activity dispatchTouchEvent
03-24 14:12:11.013 . linearLayout dispatchTouchEvent: action_move
03-24 14:12:11.013 . linearLayout onTouchListener.onTouch execut event: action_move
03-24 14:12:11.059 . activity dispatchTouchEvent
03-24 14:12:11.060 . linearLayout dispatchTouchEvent: action_move
03-24 14:12:11.060 . linearLayout onTouchListener.onTouch execut event: action_move
03-24 14:12:11.060 . activity dispatchTouchEvent
03-24 14:12:11.060 . linearLayout dispatchTouchEvent: action_up
03-24 14:12:11.060 . linearLayout onTouchListener.onTouch execut event: action_up
结果分析:事件在linearLayout的onTouchListener.ouTouch中消化完毕,不会走到dispatchTouchEvent的onTouchEvent中




情况2:
linearlayout设置了onClickListerner,在onTouchListener.ouTouch返回false
点击空白出:
03-24 14:35:08.641 . activity dispatchTouchEvent
03-24 14:35:08.672 . linearLayout dispatchTouchEvent: action_down
03-24 14:35:08.672 . linearLayout onInterceptTouchEvent: action_down
03-24 14:35:08.672 . linearLayout onTouchListener.onTouch execut event: action_down
03-24 14:35:08.688 . activity dispatchTouchEvent
03-24 14:35:08.688 . linearLayout dispatchTouchEvent: action_move
03-24 14:35:08.688 . linearLayout onTouchListener.onTouch execut event: action_move
03-24 14:35:08.689 . activity dispatchTouchEvent
03-24 14:35:08.689 . linearLayout dispatchTouchEvent: action_up
03-24 14:35:08.689 . linearLayout onTouchListener.onTouch execut event: action_up
03-24 14:35:08.691 . linearLayout onClick execut
结果分析:onTouchListener.ouTouch返回false,会走到onClick中,只要设置了onClick,事件就会被消化掉,最后在ACTION_UP的时候,执行onClick




情况3:
linearlayout没设置onClickListerner,在onTouchListener.ouTouch返回true

点击空白出:
03-24 14:41:00.720 . activity dispatchTouchEvent
03-24 14:41:00.732 . linearLayout dispatchTouchEvent: action_down
03-24 14:41:00.732 . linearLayout onInterceptTouchEvent: action_down
03-24 14:41:00.732 . linearLayout onTouchListener.onTouch execut event: action_down
03-24 14:41:00.793 . activity dispatchTouchEvent
03-24 14:41:00.794 . linearLayout dispatchTouchEvent: action_move
03-24 14:41:00.794 . linearLayout onTouchListener.onTouch execut event: action_move
03-24 14:41:00.810 . activity dispatchTouchEvent
03-24 14:41:00.810 . linearLayout dispatchTouchEvent: action_move
03-24 14:41:00.810 . linearLayout onTouchListener.onTouch execut event: action_move
03-24 14:41:00.812 . activity dispatchTouchEvent
03-24 14:41:00.812 . linearLayout dispatchTouchEvent: action_up
03-24 14:41:00.812 . linearLayout onTouchListener.onTouch execut event: action_up
结果分析:同情况1,事件在linearLayout的onTouchListener.ouTouch中消化完毕,不会将事件向上传递给activity


情况4:
linearlayout没设置onClickListerner,在onTouchListener.ouTouch返回false

点击空白出:
03-24 14:37:30.106 . activity dispatchTouchEvent
03-24 14:37:30.118 . linearLayout dispatchTouchEvent: action_down
03-24 14:37:30.118 . linearLayout onInterceptTouchEvent: action_down
03-24 14:37:30.118 . linearLayout onTouchListener.onTouch execut event: action_down
03-24 14:37:30.118 . activity onTouchEvent execut event: action_down
03-24 14:37:30.133 . activity dispatchTouchEvent
03-24 14:37:30.133 . activity onTouchEvent execut event: action_move
03-24 14:37:30.175 . activity dispatchTouchEvent
03-24 14:37:30.175 . activity onTouchEvent execut event: action_up
结果分析:linearLayout没有消化事件,所以向上传递给activity


条件2:在activity中放入一个linearlayout,在linearlayout中放入一个button
linearlayout,button都设置了onTouchListener,ouTouch的返回值在条件中具体设置


在上述条件下,再分如下情况:

情况1:
button设置了onClickListerner,在onTouchListener.ouTouch返回true
linearlayout设置了onClickListerner,在onTouchListener.ouTouch返回true

点击button:
03-24 14:54:48.110 5941-5941/. activity dispatchTouchEvent
03-24 14:54:48.110 5941-5941/. linearLayout dispatchTouchEvent: action_down
03-24 14:54:48.110 5941-5941/. linearLayout onInterceptTouchEvent: action_down
03-24 14:54:48.110 5941-5941/. button dispatchTouchEvent: action_down
03-24 14:54:48.110 5941-5941/. btn onTouchListener.onTouch execut event: action_down
03-24 14:54:48.152 5941-5941/. activity dispatchTouchEvent
03-24 14:54:48.152 5941-5941/. linearLayout dispatchTouchEvent: action_move
03-24 14:54:48.152 5941-5941/. linearLayout onInterceptTouchEvent: action_move
03-24 14:54:48.152 5941-5941/. button dispatchTouchEvent: action_move
03-24 14:54:48.152 5941-5941/. btn onTouchListener.onTouch execut event: action_move
03-24 14:54:48.247 5941-5941/. activity dispatchTouchEvent
03-24 14:54:48.247 5941-5941/. linearLayout dispatchTouchEvent: action_move
03-24 14:54:48.247 5941-5941/. linearLayout onInterceptTouchEvent: action_move
03-24 14:54:48.247 5941-5941/. button dispatchTouchEvent: action_move
03-24 14:54:48.247 5941-5941/. btn onTouchListener.onTouch execut event: action_move
03-24 14:54:48.247 5941-5941/. activity dispatchTouchEvent
03-24 14:54:48.247 5941-5941/. linearLayout dispatchTouchEvent: action_up
03-24 14:54:48.247 5941-5941/. linearLayout onInterceptTouchEvent: action_up
03-24 14:54:48.248 5941-5941/. button dispatchTouchEvent: action_up
03-24 14:54:48.248 5941-5941/. btn onTouchListener.onTouch execut event: action_up
结果分析:button的onTouchListener.onTouch返回true消化了事件,不会触发点击事件,更不会向上传递事件
linearlayout不管怎样改变设置,log结果不变



情况2:
button设置了onClickListerner,在onTouchListener.ouTouch返回false
linearlayout设置了onClickListerner,在onTouchListener.ouTouch返回true

点击button:
03-24 15:02:19.825 13097-13097/. activity dispatchTouchEvent
03-24 15:02:19.851 13097-13097/. linearLayout dispatchTouchEvent: action_down
03-24 15:02:19.852 13097-13097/. linearLayout onInterceptTouchEvent: action_down
03-24 15:02:19.852 13097-13097/. button dispatchTouchEvent: action_down
03-24 15:02:19.852 13097-13097/. btn onTouchListener.onTouch execut event: action_down
03-24 15:02:19.872 13097-13097/. activity dispatchTouchEvent
03-24 15:02:19.872 13097-13097/. linearLayout dispatchTouchEvent: action_move
03-24 15:02:19.872 13097-13097/. linearLayout onInterceptTouchEvent: action_move
03-24 15:02:19.872 13097-13097/. button dispatchTouchEvent: action_move
03-24 15:02:19.872 13097-13097/. btn onTouchListener.onTouch execut event: action_move
03-24 15:02:20.022 13097-13097/. activity dispatchTouchEvent
03-24 15:02:20.022 13097-13097/. linearLayout dispatchTouchEvent: action_up
03-24 15:02:20.022 13097-13097/. linearLayout onInterceptTouchEvent: action_up
03-24 15:02:20.022 13097-13097/. button dispatchTouchEvent: action_up
03-24 15:02:20.022 13097-13097/. btn onTouchListener.onTouch execut event: action_up
03-24 15:02:20.025 13097-13097/. btn onclick execut
结果分析:button的onTouchListener.onTouch返回false,因为设置了onClickLisTener,依然消化了事件,在up时触发onCLick,不会向上传递事件
linearlayout不管怎样改变设置,log结果不变



情况3:
button不设置onClickListerner(除此之外,还要设置clickable为false,否则还是可点击),在onTouchListener.ouTouch返回true
linearlayout设置了onClickListerner,在onTouchListener.ouTouch返回true

点击button:
03-24 15:10:52.810 26450-26450/. activity dispatchTouchEvent
03-24 15:10:52.819 26450-26450/. linearLayout dispatchTouchEvent: action_down
03-24 15:10:52.819 26450-26450/. linearLayout onInterceptTouchEvent: action_down
03-24 15:10:52.819 26450-26450/. button dispatchTouchEvent: action_down
03-24 15:10:52.820 26450-26450/. btn onTouchListener.onTouch execut event: action_down
03-24 15:10:52.852 26450-26450/. activity dispatchTouchEvent
03-24 15:10:52.852 26450-26450/. linearLayout dispatchTouchEvent: action_move
03-24 15:10:52.852 26450-26450/. linearLayout onInterceptTouchEvent: action_move
03-24 15:10:52.852 26450-26450/. button dispatchTouchEvent: action_move
03-24 15:10:52.852 26450-26450/. btn onTouchListener.onTouch execut event: action_move
03-24 15:10:52.937 26450-26450/. activity dispatchTouchEvent
03-24 15:10:52.937 26450-26450/. linearLayout dispatchTouchEvent: action_move
03-24 15:10:52.937 26450-26450/. linearLayout onInterceptTouchEvent: action_move
03-24 15:10:52.937 26450-26450/. button dispatchTouchEvent: action_move
03-24 15:10:52.937 26450-26450/. btn onTouchListener.onTouch execut event: action_move
03-24 15:10:52.937 26450-26450/. activity dispatchTouchEvent
03-24 15:10:52.937 26450-26450/. linearLayout dispatchTouchEvent: action_up
03-24 15:10:52.937 26450-26450/. linearLayout onInterceptTouchEvent: action_up
03-24 15:10:52.937 26450-26450/. button dispatchTouchEvent: action_up
03-24 15:10:52.937 26450-26450/. btn onTouchListener.onTouch execut event: action_up
结果分析:button的onTouchListener.onTouch返回true消化了事件,不会向上传递事件
linearlayout不管怎样改变设置,log结果不变



情况4:
button不设置onClickListerner(除此之外,还要设置clickable为false,否则还是可点击),在onTouchListener.ouTouch返回false
linearlayout设置了onClickListerner,在onTouchListener.ouTouch返回true

点击button:
03-24 15:14:56.441 31668-31668/. activity dispatchTouchEvent
03-24 15:14:56.442 31668-31668/. linearLayout dispatchTouchEvent: action_down
03-24 15:14:56.442 31668-31668/. linearLayout onInterceptTouchEvent: action_down
03-24 15:14:56.442 31668-31668/. button dispatchTouchEvent: action_down
03-24 15:14:56.442 31668-31668/. btn onTouchListener.onTouch execut event: action_down
03-24 15:14:56.442 31668-31668/. linearLayout onTouchListener.onTouch execut event: action_down
03-24 15:14:56.522 31668-31668/. activity dispatchTouchEvent
03-24 15:14:56.522 31668-31668/. linearLayout dispatchTouchEvent: action_move
03-24 15:14:56.523 31668-31668/. linearLayout onTouchListener.onTouch execut event: action_move
03-24 15:14:56.584 31668-31668/. activity dispatchTouchEvent
03-24 15:14:56.584 31668-31668/. linearLayout dispatchTouchEvent: action_move
03-24 15:14:56.584 31668-31668/. linearLayout onTouchListener.onTouch execut event: action_move
03-24 15:14:56.584 31668-31668/. activity dispatchTouchEvent
03-24 15:14:56.584 31668-31668/. linearLayout dispatchTouchEvent: action_up
03-24 15:14:56.584 31668-31668/. linearLayout onTouchListener.onTouch execut event: action_up
结果分析:其实这个时候变为了条件A的情况1,button的这种设置,其实就是条件A了,只是ACTION_DOWN时事件回传递到button


总结:
1.activity,linearlayout,button模型中,事件总是从activity调用dispatchTouchEvent开始分发,然后到linearLayout又开始分发并判断是否拦截事件
2.事件分发到button后,如果button不处理就向上传递给linearlayout,linearlayout不处理就向上传递给activity,一旦一个节点处理了,事件不会向上传递
3.onTouchListener.onTouch返回ture,或者设置了onClickListener,事件就会被拦截处理,而不会向上传递了



想到之前某一个博客上的类比:
类似中央,省,市,县
命令是一级一级下发,最后到县,县里处理好了,那市里就不用处理,如果县里没处理好,那就该市里处理,以此类推。
在down,move,up这样一套动作里,如果你down处理好了,后续的move和up还会给你处理。如果没处理好,后续的动作也不会给你处理了。
下一套动作再从头开始以上的逻辑。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android触摸事件分发是指在屏幕上发生触摸事件时,Android系统将该事件分发给适当的视图处理。触摸事件分发的过程涉及多个层级,包括Activity、ViewGroup和View。 当用户触摸屏幕时,Android系统首先将触摸事件发送给当前活动的Window。然后,Window将事件传递给顶级ViewGroup(通常是根布局),该ViewGroup负责协调子视图的事件处理。 在ViewGroup中,触摸事件会按照一定的规则进行分发。常见的分发方式有以下几种: 1. 捕获阶段(Capture Phase):从根布局向下遍历,让父级ViewGroup有机会拦截事件。可以通过重写`onInterceptTouchEvent()`方法来实现事件的拦截。 2. 目标阶段(Target Phase):如果没有被拦截,触摸事件将传递给目标View,即最终接收事件的视图。目标View将调用`onTouchEvent()`方法处理事件。 3. 冒泡阶段(Bubble Phase):如果目标View没有消耗事件事件将向上传递给父级ViewGroup,直到根布局。在这个阶段,可以通过返回值来控制是否继续向上传递。 除了上述的默认分发方式外,还可以通过重写`dispatchTouchEvent()`方法来自定义事件分发逻辑。通过调用`super.dispatchTouchEvent()`来保持默认行为,或者根据需求进行处理。 总结来说,Android触摸事件分发涉及捕获阶段、目标阶段和冒泡阶段,通过重写相关方法或自定义分发逻辑,可以实现对触摸事件的处理和控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值