侧边栏壁纸
博主头像
慢行的骑兵博主等级

贪多嚼不烂,欲速则不达

  • 累计撰写 29 篇文章
  • 累计创建 27 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

事件分发机制

慢行的骑兵
2021-09-23 / 0 评论 / 0 点赞 / 307 阅读 / 1,261 字

一.前言

  • 事件最开始是从屏幕驱动得来的,屏幕驱动调到了Linux层,linux层调到了WindowManagerService,再调到Activity,再调用PhoneWindow;

  • 事件分发:在面试方面几乎是必考题,可以说对事件了解多深,对自定义控件开发水平就有多高。事件不处理好,各种时间冲突,卡顿是家常便饭。

  • 学习事件分发的目的

    • 面试容易考;
    • 开发自定义控件容易用到;
    • 排除问题比较复杂;
    • 熟悉Android事件机制有助于理解Android系统;
  • 手机接收产生事件的本质

    • 手机屏幕得到用户点击的坐标,经过电容传递给传感器,传感器转换成电频传递给电路板,电路板再传递给Linux层,Linux通过jni接口将坐标写入到WindowManagerService的队列(坐标封装成Event事件),WindowManagerService就会去读取队列中的坐标,然后分发给Activity;

  • MotionEvent的产生:通过屏幕驱动,由底层产生的;
  • WindowManagerService:是一个窗口管理系统服务,它的主要功能包含如下
    • 窗口管理,绘制
    • 转场动画--Activity切换动画
    • Z-ordered的维护
    • Activity窗口显示前后顺序
    • 输入法管理
    • 系统事件分发线程
  • 源码部分(sdk版本为28),我们只需要看Activity相关的即可,jni层没必要去看;

二.Activity的dispatchTouchEvent方法

//通过注释可以了解到
//该方法是被用户触摸屏幕的进程(可以理解成WindowManagerService)去调用
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    //调用PhoneWindow的superDispatchTouchEvent方法
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}
  • WindowManagerService:其是一个独立的进程(管理手机app窗体的),内部有一个main函数,我们需要明白的是WindowManagerService会主动调用Activity的dispatchTouchEvent方法即可;
  • 分析PhoneWindow的superDispatchTouchEvent方法
public boolean superDispatchTouchEvent(MotionEvent event) {
    //调用DecorView的superDispatchTouchEvent方法
    return mDecor.superDispatchTouchEvent(event);
}

//DecorView类的superDispatchTouchEvent方法
public boolean superDispatchTouchEvent(MotionEvent event) {
    //调用ViewGroup的dispatchTouchEvent方法
    return super.dispatchTouchEvent(event);
}
  • 到此,我们先停下来,建议大家直接学习Android事件分发机制 详解攻略,您值得拥有,我不太愿意花大量时间重复造轮子。关于这篇文章,是非常的优秀,但是我还是提出一些个人的建议,没有提到递归思想(如果不懂递归,直接看作者画的图,会有点晕),我们一定要懂的什么是递归,实际上我们分析事件分发的源码,实际上只分析Activity的dispatchTouchEvent方法,但是内部涉及到方法的嵌套调用,牵扯的内容就非常多,一些逻辑需要回溯。另外,为什么ViewGroup的拦截方法只会走一次等等;
  • 另外还需要学习图解 Android 事件分发机制,这篇文章更优,很有必要学习一下;
  • 第三部分,我决定补充文章中未提及到的细节和注意事项;

三.其它

1.什么时候产生cancel事件

  • 点击控件的时候,移出到父容器之外了。

3.为什么ViewGroup的拦截方法只会走一次

  • ViewGroup的dispatchTouch-Event方法中
//ViewGroup在如下两种情况下会判断是否要拦截当前事件:事件类型为ACTION_DOWN或者mFirstTouchTarget ! = null
//ACTION_DOWN事件好理解,那么mFirstTouchTarget ! = null是什么意思呢?这个从后面的代码逻辑可以看出来,当事件由ViewGroup的子元素成功处理时,mFirstTouchTarget会被赋值并指向子元素,换种方式来说,当ViewGroup不拦截事件并将事件交由子元素处理时mFirstTouchTarget ! = null。反过来,一旦事件由当前ViewGroup拦截时,mFirstTouchTarget ! = null就不成立
//那么当ACTION_MOVE和ACTION_UP事件到来时,由于(actionMasked ==MotionEvent. ACTION_DOWN || mFirstTouchTarget ! = null)这个条件为false,将导致ViewGroup的onInterceptTouchEvent不会再被调用,并且同一序列中的其他事件都会默认交给它处理。

3.其它

  • 可以多个view响应down事件;
  • onClick会发生的前提是当前View是可点击的,并且它收到了down和up的事件(可自行用手机app测试);
  • 那么当ACTION_MOVE和ACTION_UP事件到来时,由于(actionMasked ==MotionEvent. ACTION_DOWN || mFirstTouchTarget ! = null)这个条件为false,将导致ViewGroup的onInterceptTouchEvent不会再被调用,并且同一序列中的其他事件都会默认交给它处理;
  • mFirstTouchTarget其实是一种单链表结构。mFirstTouchTarget是否被赋值,将直接影响到ViewGroup对事件的拦截策略;
  • 通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外;
0

评论区