Android 事件分发详情

  1. Touch事件如何从屏幕到我们的App
    最开始是硬件驱动获取到设备,当系统启动时,在SystemServer进程会启动一系列系统服务,如AMS,WMS等。
    其中还有一个就是我们管理事件输入的InputManagerService。这个服务就是用来负责与硬件通信,接受屏幕输入事件。
    最终在SystemServer进程中,WindowManagerService根据当前的Window创建了SocketPair用于跨进程通信,App进程的主线程就会监听socket客户端,当收到消息(输入事件)后,回调NativeInputEventReceiver.handleEvent()方法,最终会走到InputEventReceiver.dispachInputEvent方法。经过以上操作App终于拿到输入事件了

2.事件在页面上的分发
Android的事件分发机制相关的类:
public boolean dispatchTouchEvent(event):用于进行点击事件的分发
public boolean onInterceptTouchEvent(event):用于进行点击事件的拦截
public boolean onTouchEvent(event):用于处理点击事件

三个函数的参数均为even,即上面所说的3种类型的输入事件,返回值均为boolean 类型
上面的三种方法的调用关系大致可以用下面的伪代码来描述
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;//事件是否被消费
if (onInterceptTouchEvent(ev)){//调用onInterceptTouchEvent判断是否拦截事件
consume = onTouchEvent(ev);//如果拦截则调用自身的onTouchEvent方法
}else{
consume = child.dispatchTouchEvent(ev);//不拦截调用子View的dispatchTouchEvent方法
}
return consume;//返回值表示事件是否被消费,true事件终止,false调用父View的onTouchEvent方法
}

下面详细的说说这3个方法:
dispatchTouchEvent()是处理触摸事件分发,事件(多数情况)是从Activity的dispatchTouchEvent()开发的;执行getWindow().superDispatchTouchEvent(ev)事件向下分发;

Activity    
public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        //调用Window下的superDispatchTouchEvent()方法
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
 
PhoneWindow
    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        //调用Window窗口下的DecorView的superDispatchTouchEvent()方法
        return mDecor.superDispatchTouchEvent(event);
    }
 
DecorView
    public boolean superDispatchTouchEvent(MotionEvent event) {
        //调用DecorView父视图的super.dispatchTouchEvent()方法
        return super.dispatchTouchEvent(event);
    }
 
ViewGroup
public boolean dispatchTouchEvent(MotionEvent event) {
     ...
     handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
     ...
     return handled;
}
 
 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        ...
        //事件向子视图传递
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        ...
        return handled;
}

onInterceptTouchEvent()是ViewGroup提供的方法,默认返回false,返回true表示拦截;
onTouchEvent()是View中提供的方法,ViewGroup也有这个方法,view中不提供onInterceptTouchEvent();view中默认返回true,表示消费了这个事件;

View里有两个事件回调函数

public boolean dispatchTouchEvent(MotionEvent ev);

public boolean onTouchEvent(MotionEvent ev);

ViewGroup里,有三个回调函数

public boolean dispatchTouchEvent(MotionEvent ev);  
public boolean onInterceptTouchEvent(MotionEvent ev);  
public boolean onTouchEvent(MotionEvent ev);

Activity里有两个回调函数

public boolean dispatchTouchEvent(MotionEvent ev);  
public boolean onTouchEvent(MotionEvent ev);

事件的分发流程:
3.事件传递流程分析

Android中默认情况下事件传递是由最终的View的接收到,传递过程是从父视图到子视图,也就是从Activity到ViewGroup到View的过程,默认情况下,ViewGroup起到透传的作用;Android中事件传递过程(按箭头方向)处理,如下图在这里插入图片描述

触摸事件是一连串ACTION_DOWN,ACTION_MOVE…MOVE,最后ACTION_UP,触摸事件还有ACTION_CANCEL事件,事件都是从ACTION_DOWN开始的,Activity的dispatchTouchEvent()首先接收到ACTION_DOWN,执行super.dispatchTouchEvent()或者child.dispatchTouchEvent(event),事件向下分发;

dispatchTouchEvent()返回true,后续事件(ACTION_MOVE,ACTION_UP)会在传递,如果返回false,dispatchTouchEvent()就接收不到ACTION_UP,ACTION_MOVE事件;

事件传递顺序如下:
拦截情况我们可以概括为已下几种可能:
1.ACTION_DOWN都没被消费
2.ACTION_DOWM在后面被消费
3.ACTION_DOWN被消费后,后续的UP和MOVE 不被拦截
4.ACTION_DOWN被消费后,后续的UP和MOVE 被拦截的情况
5.ACTION_DOWN一开始就被拦截
下面对 以上不同的情况详细说明:

1.ACTION_DOWN都没被消费
在这里插入图片描述

2.ACTION_DOWN被View消费了
在这里插入图片描述

3.后续ACTION_MOVE和UP在不被拦截的情况下都会去找VIEW

在这里插入图片描述

4.后续的被拦截了
在这里插入图片描述

5.ACTION_DOWN一开始就被拦截

在这里插入图片描述

Android中的Touch事件都是从ACTION_DOWN开始的:

单手指操作:ACTION_DOWN—ACTION_MOVE----ACTION_UP

多手指操作:ACTION_DOWN—ACTION_POINTER_DOWN—ACTION_MOVE–ACTION_POINTER_UP—ACTION_UP

4.冲突事件处理

1.根据父布局中的onInterceptToucherEven(),判断子布局 如果在滑动,就不做拦截

2.在子布局中 的 dispatchTouchEvent中 判断自己滑动,如果有滑动 请求父布局不要拦截自己 getparent.requestDisallowInterceptToucherEvent(true||false);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值