Android消息类型及事件分发流程

消息类型

用户消息类型指Wms将硬件物理消息转化成统一格式消息,分为三类:按键消息、触摸消息和轨迹球消息(此消息API的Demo中可见,游戏中比较常见)。而消息的组成由以下三项:Action(上和下)、KeyCode(键代码0-9a-z)、Repeat(重复次数)。

消息先由DecorView处理,如果不处理,则分发到下面的ViewGroup和View;如果还没处理则上传给PhoneWindow,最后给Activity.

另外关于消息传递,通过InputDispatcherThread来执行,由InputReader读取,通过InputChannel,由InputDispatcher来传递,

最后调用ViewRoot的dispatchMotion和dispatchKey来传递给页面。

Binder传输性能高、安全性高、CS架构,通信由Client、ServiceManger、Binder驱动和Server组成,只有驱动在内核空间,其他均在用户空间。Android 系统是事件驱动的,所以这个 Looper 是用来接收应用事件的.

Acitivty中的事件分发流程:

1.ViewRootImpl层的事件分发会首先调用Activity的dispatchTouchEvent方法;

2.Activity的dispatchTouchEvent方法中会通过Window.superDispatchTouchEvent方法将事件传递给DecorView即ViewGroup。

3.若window的superDispatchTouchEvent方法返回true,则事件分发完成,Activity的dispatchTouchEvent直接返回为true,否则的话调用Activity的onTouchEvent方法,并且Acitivty的dispatchTouchEvent返回值与Activity的onTouchEvent返回值一致。

Android View系统解析(上)- http://blog.csdn.net/singwhatiwanna/article/details/38168103

Android View系统解析(下)- http://blog.csdn.net/singwhatiwanna/article/details/38426471

-- View的绘制流程包含了测量大小,测量位置,绘制三个流程;

Activty的界面绘制是从mDector即根View开始的,也就是从mDector的测量大小,测量位置,绘制三个流程;

View体系的绘制流程是从ViewRootImpl的performTraversals方法开始的;

View的测量大小流程:performMeasure --> measure --> onMeasure等方法;

View的测量位置流程:performLayout --> layout --> onLayout等方法;

View的绘制流程:performDraw --> draw --> onDraw等方法;

-- View组件分发触摸事件的时候:

1.View控件会首先执行dispatchTouchEvent方法。

2.View控件在dispatchTouchEvent方法中先执行onTouch方法,后执行onClick方法。

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

4.View控件不是enable的,那么即使设置了onTouch方法也不会执行,只能通过重写控件的onTouchEvent方法处理,dispatchTouchEvent返回值与onTouchEvent返回一样。

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

cpp 复制代码
/**
  * 源码分析:ViewGroup.dispatchTouchEvent()
  */ 
  public boolean dispatchTouchEvent(MotionEvent ev) { 

  // 仅贴出关键代码
  ... 

  if (disallowIntercept || !onInterceptTouchEvent(ev)) {  
  // 分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
    // 判断值1-disallowIntercept:是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改
    // 判断值2-!onInterceptTouchEvent(ev) :对onInterceptTouchEvent()返回值取反
        // a. 若在onInterceptTouchEvent()中返回false,即不拦截事件,从而进入到条件判断的内部
        // b. 若在onInterceptTouchEvent()中返回true,即拦截事件,从而跳出了该条件判断
        // c. 关于onInterceptTouchEvent() ->>分析1

  // 分析2
    // 1. 通过for循环,遍历当前ViewGroup下的所有子View
    for (int i = count - 1; i >= 0; i--) {  
        final View child = children[i];  
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
                || child.getAnimation() != null) {  
            child.getHitRect(frame);  

            // 2. 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View
            if (frame.contains(scrolledXInt, scrolledYInt)) {  
                final float xc = scrolledXFloat - child.mLeft;  
                final float yc = scrolledYFloat - child.mTop;  
                ev.setLocation(xc, yc);  
                child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  

                // 3. 条件判断的内部调用了该View的dispatchTouchEvent()
                // 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面章节介绍的View事件分发机制)
                if (child.dispatchTouchEvent(ev))  { 

                // 调用子View的dispatchTouchEvent后是有返回值的
                // 若该控件可点击,那么点击时dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立
                // 于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出
                // 即该子View把ViewGroup的点击事件消费掉了

                mMotionTarget = child;  
                return true; 
                      }  
                  }  
              }  
          }  
      }  
    }  

  ...

  return super.dispatchTouchEvent(ev);
  // 若无任何View接收事件(如点击空白处)/ViewGroup本身拦截了事件(复写了onInterceptTouchEvent()返回true)
  // 会调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
  // 因此会执行ViewGroup的onTouch() -> onTouchEvent() -> performClick() -> onClick(),即自己处理该事件,事件不会往下传递
  // 具体请参考View事件分发机制中的View.dispatchTouchEvent()

  ... 

}

/**
  * 分析1:ViewGroup.onInterceptTouchEvent()
  * 作用:是否拦截事件
  * 说明:
  *     a. 返回false:不拦截(默认)
  *     b. 返回true:拦截,即事件停止往下传递(需手动复写onInterceptTouchEvent()其返回true)
  */
  public boolean onInterceptTouchEvent(MotionEvent ev) {  
    
    // 默认不拦截
    return false;

  } 
  // 回到调用原处
cpp 复制代码
/**
 * 源码分析:View.dispatchTouchEvent()
 */
public boolean dispatchTouchEvent(MotionEvent event) {


        if ( (mViewFlags & ENABLED_MASK) == ENABLED &&
        mOnTouchListener != null &&
        mOnTouchListener.onTouch(this, event)) {
        return true;
        }

        return onTouchEvent(event);
        }


/**
 * 分析1:onTouchEvent()
 */
public boolean onTouchEvent(MotionEvent event) {

        ... // 仅展示关键代码

        // 若该控件可点击,则进入switch判断中
        if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {

        // 根据当前事件类型进行判断处理
        switch (event.getAction()) {

        // a. 事件类型=抬起View(主要分析)
        case MotionEvent.ACTION_UP:
        performClick();
        // ->>分析2
        break;

        // b. 事件类型=按下View
        case MotionEvent.ACTION_DOWN:
        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
        break;

        // c. 事件类型=结束事件
        case MotionEvent.ACTION_CANCEL:
        refreshDrawableState();
        removeTapCallback();
        break;

        // d. 事件类型=滑动View
        case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();

        int slop = mTouchSlop;
        if ((x < 0 - slop) || (x >= getWidth() + slop) ||
        (y < 0 - slop) || (y >= getHeight() + slop)) {
        removeTapCallback();
        if ((mPrivateFlags & PRESSED) != 0) {
        removeLongPressCallback();
        mPrivateFlags &= ~PRESSED;
        refreshDrawableState();
        }
        }
        break;
        }

        // 若该控件可点击,就一定返回true
        return true;
        }
        // 若该控件不可点击,就一定返回false
        return false;
        }

/**

* 源码分析:ViewGroup.dispatchTouchEvent()

*/

public boolean dispatchTouchEvent(MotionEvent ev) {

// 仅贴出关键代码

...

if (disallowIntercept || !onInterceptTouchEvent(ev)) {

// 分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件

// 判断值1-disallowIntercept:是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改

// 判断值2-!onInterceptTouchEvent(ev) :对onInterceptTouchEvent()返回值取反

// a. 若在onInterceptTouchEvent()中返回false,即不拦截事件,从而进入到条件判断的内部

// b. 若在onInterceptTouchEvent()中返回true,即拦截事件,从而跳出了该条件判断

// c. 关于onInterceptTouchEvent() ->>分析1

// 分析2

// 1. 通过for循环,遍历当前ViewGroup下的所有子View

for (int i = count - 1; i >= 0; i--) {

final View child = children[i];

if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE

|| child.getAnimation() != null) {

child.getHitRect(frame);

// 2. 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View

if (frame.contains(scrolledXInt, scrolledYInt)) {

final float xc = scrolledXFloat - child.mLeft;

final float yc = scrolledYFloat - child.mTop;

ev.setLocation(xc, yc);

child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;

// 3. 条件判断的内部调用了该View的dispatchTouchEvent()

// 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面章节介绍的View事件分发机制)

if (child.dispatchTouchEvent(ev)) {

// 调用子View的dispatchTouchEvent后是有返回值的

// 若该控件可点击,那么点击时dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立

// 于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出

// 即该子View把ViewGroup的点击事件消费掉了

mMotionTarget = child;

return true;

}

}

}

}

}

}

...

return super.dispatchTouchEvent(ev);

// 若无任何View接收事件(如点击空白处)/ViewGroup本身拦截了事件(复写了onInterceptTouchEvent()返回true)

// 会调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()

// 因此会执行ViewGroup的onTouch() -> onTouchEvent() -> performClick() -> onClick(),即自己处理该事件,事件不会往下传递

// 具体请参考View事件分发机制中的View.dispatchTouchEvent()

...

}

/**

* 分析1:ViewGroup.onInterceptTouchEvent()

* 作用:是否拦截事件

* 说明:

* a. 返回false:不拦截(默认)

* b. 返回true:拦截,即事件停止往下传递(需手动复写onInterceptTouchEvent()其返回true)

*/

public boolean onInterceptTouchEvent(MotionEvent ev) {

// 默认不拦截

return false;

}

// 回到调用原处

/**

* 源码分析:View.dispatchTouchEvent()

*/

public boolean dispatchTouchEvent(MotionEvent event) {

if ( (mViewFlags & ENABLED_MASK) == ENABLED &&

mOnTouchListener != null &&

mOnTouchListener.onTouch(this, event)) {

return true;

}

return onTouchEvent(event);

}

/**

* 分析1:onTouchEvent()

*/

public boolean onTouchEvent(MotionEvent event) {

... // 仅展示关键代码

// 若该控件可点击,则进入switch判断中

if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {

// 根据当前事件类型进行判断处理

switch (event.getAction()) {

// a. 事件类型=抬起View(主要分析)

case MotionEvent.ACTION_UP:

performClick();

// ->>分析2

break;

// b. 事件类型=按下View

case MotionEvent.ACTION_DOWN:

postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());

break;

// c. 事件类型=结束事件

case MotionEvent.ACTION_CANCEL:

refreshDrawableState();

removeTapCallback();

break;

// d. 事件类型=滑动View

case MotionEvent.ACTION_MOVE:

final int x = (int) event.getX();

final int y = (int) event.getY();

int slop = mTouchSlop;

if ((x < 0 - slop) || (x >= getWidth() + slop) ||

(y < 0 - slop) || (y >= getHeight() + slop)) {

removeTapCallback();

if ((mPrivateFlags & PRESSED) != 0) {

removeLongPressCallback();

mPrivateFlags &= ~PRESSED;

refreshDrawableState();

}

}

break;

}

// 若该控件可点击,就一定返回true

return true;

}

// 若该控件不可点击,就一定返回false

return false;

}

相关推荐
REDcker34 分钟前
Android WebView 版本升级方案详解
android·音视频·实时音视频·webview·js·编解码
麦兜*36 分钟前
【springboot】图文详解Spring Boot自动配置原理:为什么@SpringBootApplication是核心?
android·java·spring boot·spring·spring cloud·tomcat
le1616161 小时前
Android 依赖种类及区别:远程仓库依赖、打包依赖、模块依赖、本地仓库依赖
android
lxysbly1 小时前
psp模拟器安卓版带金手指
android
云上凯歌2 小时前
02 Spring Boot企业级配置详解
android·spring boot·后端
hqiangtai2 小时前
Android 高级专家技术能力图谱
android·职场和发展
aqi002 小时前
FFmpeg开发笔记(九十七)国产的开源视频剪辑工具AndroidVideoEditor
android·ffmpeg·音视频·直播·流媒体
stevenzqzq2 小时前
Android Koin 注入入门教程
android·kotlin
炼金术3 小时前
SkyPlayer v1.1.0 - 在线视频播放功能更新
android·ffmpeg
用户276038157813 小时前
鲲鹏+昇腾:开启 AI for Science 新范式——基于PINN的流体仿真加速实践
android