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;

}

相关推荐
小白也想学C3 分钟前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程10 分钟前
初级数据结构——树
android·java·数据结构
闲暇部落2 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX4 小时前
Android 分区相关介绍
android
大白要努力!5 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee5 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood6 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-9 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen11 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年18 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin