Android中的事件其实就是一个触摸事件或者案件事件,是通过MotionEvent这个对象进行传递的,所谓对事件的分发其实就是对MotionEvent的分发,当一个事件产生后,系统会把这个事件分发到一个具体的View对象。Activity是一个层级的结构,它的事件分发起点是Activity,终点是末端的view,如果view不处理该事件,事件又会以一个反向的过程传递。
在这里插入图片描述
上图是Android的窗口结构,其实window是通过一个叫ViewRootImpl的对象来管理整个view树的。
在Activity中事件分发是从函数dispatchTouchEvent()开始的

    public boolean dispatchTouchEvent(MotionEvent ev) {
    
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

上面代码我们重点关注getWindow().superDispatchTouchEvent(ev),Activity将事件分发到自己的window对象中去处理,因为Activity中Window的具体实现是PhoneWindow,所以我们接着去看PhoneWindow中是怎么处理的

    @Override
    public boolean superDispatchKeyEvent(KeyEvent event) {
        return mDecor.superDispatchKeyEvent(event);
    }

window 将事件分发给了mDecor对象去处理,上面的图中可以看出,DecorView可以看做是整个View树根,事件从这里开始向下传递。
在view中事件传递这个过程主要借助三个函数完成的
dispatchTouchEvent(), onTouchEvent()和onInterceptTouchEvent(),下面介绍一下这三个函数的大体功能。

  • dispatchTouchEvent(MotionEvent e)
    如果事件被分发到了当前的View(ViewGroup也是View),那这个函数必定会被调用,它的返回结果受自身的onTouchEvent()和下级View影响。如果返回false表示没有消耗该事件,否则消耗了该事件。
  • onTouchEvent(MotionEvent e)
    在这个方法中处理事件,如果返回了false,则一个事件序列中的其他事件不会再分发到这里。
  • onInterceptTouchEvent(MotionEvent e)
    这个方法在View中是没有的,只在ViewGroup中有,表示是否拦截该事件,如果拦截则不会再向下传递。如果拦截了该事件,同一个事件中的事件不会再调用到这里。

那这三个方法有什么关系呢?这里借用Android开发艺术这本书上的一段伪代码来描述一下

public boolean dispatchTouchEvent(MotionEvent e){
	boolean consume = false;
	if(onInterceptTouchEvent(e)){
		consume = onTouchEvent(e);
	}else{
		consume = child.dispatchTouchEvent(e);
	}
	return consume;
}

从上面的伪代码我们知道事件处理的大体流程是,如果当前的View决定拦截该事件,那么会调用自身的onTouchEvent()进行处理,事件不向下传递;如果当前view不拦截该事件,那么会调用子view的dispatchTouchEvent()方法将事件分发给自己的子view去处理,如果子view不处理,也就是返回false,那这个事件又会向上传递。
下面我们总结一下事件分发机制的一些特点

  • 同一个事件序列指的是,从手指触摸屏幕到抬起的国产中产生的事件,包括一个Down事件,0-多个move事件,一个UP事件。
  • 一个事件序列不能同时交给多个View同时处理,一旦一个ViewGroup拦截了某个事件,那么这个事件序列中的其他事件都会交给他来处理,并且该事件序列中的其他事件不会再调用onInterceptTouchEvent来决定是否拦截。
  • 如果onTouchEvent中处理Down事件时返回了false,就表示它不处理该事件,那么同一个事件序列中的其他事件就不会再交给它来处理,会将事件重新交给它的父元素处理。
  • ViewGroup默认不拦截任何事件
  • view默认会消耗事件,即onTouchEvent返回true, 除非这个view是不可以点击的,即clickable和longClickable都是false.
  • 事件传递是由内到外的,即Activity->Window->View,如果最底层的view不处理,那么事件会返回给上一层处理。
  • child中requestDisallowInterceptTouchEvent() 可以在子元素中干预父元素的事件分发过程,但是无法干预 ACTION_DOWN 事件。
  • 可以给view设置监听器检测触摸事件,它们的调用优先级顺序是这样的setOnTouchListener() => onTouchEvent() => onClickListener()
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐