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

发布于:2024-09-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

## 消息类型
  用户消息类型指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方法。

/**
  * 源码分析: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;
        }

 

 

/**
  * 源码分析: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;
        }
 


网站公告

今日签到

点亮在社区的每一天
去签到