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

news2024/9/17 3:36:02

## 消息类型
  用户消息类型指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;
        }
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2114786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis典型应用 - 分布式锁

文章目录 目录 文章目录 1. 什么是分布式锁 2. 分布式锁的基本实现 3. 引入过期时间 4. 引入校验Id 5. 引入 watch dog(看门狗) 6. 引入redlock算法 工作原理 Redlock的优点&#xff1a; 总结 1. 什么是分布式锁 在一个分布式系统中,也可能会出现多个节点访问一个共…

QT 编译报错:C3861: ‘tr‘ identifier not found

问题&#xff1a; QT 编译报错&#xff1a;C3861&#xff1a; ‘tr’ identifier not found 原因 使用tr的地方所在的类没有继承自 QObject 类 或者在不在某一类中&#xff0c; 解决方案 就直接用类名引用 &#xff1a;QObject::tr( )

关于易优cms自定义字段不显示的问题

今天在该易优cms自定义字段&#xff0c;折腾了大半天没显示出来&#xff0c;原来是改错对方了。 主要引用的时候 要放在list标签内&#xff0c;不要看文档&#xff0c;把addfields 放在list标签上 例如 {eyou:list loop8} <li><a href"{$field.arcurl}">…

基于yolov8的电动车佩戴头盔检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的电动车佩戴头盔检测系统利用了YOLOv8这一先进的目标检测模型&#xff0c;旨在提高电动车骑行者的安全意识&#xff0c;减少因未佩戴头盔而导致的交通事故风险。YOLOv8作为YOLO系列的最新版本&#xff0c;在检测速度和精度上均进行了优化&#xff0c;…

✨机器学习笔记(一)—— 监督学习和无监督学习

1️⃣ 监督学习&#xff08;supervised learning&#xff09; ✨ 两种主要类型的监督学习问题&#xff1a; 回归&#xff08;regression&#xff09;&#xff1a;predict a number in infinitely many possible outputs. 分类&#xff08;classification&#xff09;&#xff1…

C#串口助手初级入门

1.创建项目 修改项目名称与位置&#xff0c;点击创建 2.进入界面 在视图中打开工具箱&#xff0c;鼠标拖动&#xff0c;便可以在窗口添加控件&#xff0c;右边可以查看与修改属性 3.解决方案资源管理器 发布之前&#xff0c;需要修改相关的信息&#xff0c;比如版本号&#x…

Lombok jar包引入和用法

大家好&#xff0c;今天分享一个在编写代码时的快捷方法。 当我们在封装实体类时&#xff0c;会使用set、get等一些方法。如下图&#xff0c;不但费事还影响代码的美观。 那么如何才能减少代码的冗余呢&#xff0c;首先lib中导入lombok的jar包并添加库。 此处我已导入&#xf…

插件:清理maven错误缓存.bat

插件&#xff1a;https://pan.baidu.com/s/1nHIxHoo1C4MvFlW7QbZe5Q?pwd7zenhttps://pan.baidu.com/s/1nHIxHoo1C4MvFlW7QbZe5Q?pwd7zen没错误缓存时&#xff1a; 有错误缓存时&#xff1a;

真实案例分享:零售企业如何避免销售数据的无效分析?

在零售业务的数据分析中&#xff0c;无效分析不仅浪费时间和资源&#xff0c;还可能导致错误的决策。为了避免这种情况&#xff0c;企业必须采取策略来确保他们的数据分析工作能够产生实际的商业价值。本文将通过行业内真实的案例&#xff0c;探讨零售企业如何通过精心设计的数…

springboot数据库连接由localhost改成IP以后访问报错500(2024/9/7

步骤很详细&#xff0c;直接上教程 情景复现 一.没改为IP之前正常 二.改完之后报错 问题分析 SQL没开启远程连接权限 解决方法 命令行登入数据库 mysql -u root -p切换到对应数据库 use mysql;设置root用户的连接权限允许其他IP连接数据库 update user set host % whe…

jmeter执行python脚本,python脚本的Faker库

jmeter安装 jython的插件jar包 通过如下地址下载jython-standalone-XXX.jar包并放到jmeter的XXX\lib\ext目录下面 Downloads | JythonThe Python runtime on the JVMhttps://www.jython.org/download.html 重启jmeter在JSR223中找到jython可以编写python代码执行 python造数据…

一种快速生成CSV的方法

事情是这个样子的 在QQ群在聊把如何100万数据导出成CSV文件&#xff1f;会不会很慢&#xff1f; 俺回了一句“现在的机器性能好&#xff0c;没啥问题”。 然后大家开始谈论机器的配置了。哎&#xff0c;俺的机器配置有点差。 然后俺就进行了一个测试。 测试数据 数据定义…

【C++二分查找】2439. 最小化数组中的最大值

本文涉及的基础知识点 C二分查找 LeetCode2439. 最小化数组中的最大值 给你一个下标从 0 开始的数组 nums &#xff0c;它含有 n 个非负整数。 每一步操作中&#xff0c;你需要&#xff1a; 选择一个满足 1 < i < n 的整数 i &#xff0c;且 nums[i] > 0 。 将 num…

C++ | Leetcode C++题解之第392题判断子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {int n s.size(), m t.size();vector<vector<int> > f(m 1, vector<int>(26, 0));for (int i 0; i < 26; i) {f[m][i] m;}for (int i m - 1; …

.Net6/.Net8(.Net Core) IIS中部署 使用 IFormFile 上传大文件报错解决方案

描述 最近使用.Net6 WebAPI IFormFile对象接收上传文件时大于30MB(兆)的文件就会报错 原因分析 IIS上传文件有大小默认限制大约28.6MB 解决办法 .无论是Net6还是.Net8写法都一样 方法一&#xff1a;IIS可视化操作 1.打开Internet Information Services (llS)管理器&…

Banana Pi BPI-SM9 AI 计算模组采用算能科技BM1688芯片方案设计

产品概述 香蕉派 Banana Pi BPI-SM9 16-ENC-A3 深度学习计算模组搭载算能科技高集成度处理器 BM1688&#xff0c;功耗低、算力强、接口丰富、兼容性好。支持INT4/INT8/FP16/BF16/FP32混合精度计算&#xff0c;可支持 16 路高清视频实时分析&#xff0c;灵活应对图像、语音、自…

LeetCode --- 413周赛

题目列表 3274. 检查棋盘方格颜色是否相同 3275. 第 K 近障碍物查询 3276. 选择矩阵中单元格的最大得分 3277. 查询子数组最大异或值 一、检查棋盘方格颜色是否相同 题目给定两个字符串来表示两个方格的坐标&#xff0c;让我们判断这两个方格的颜色是否相同&#xff0c;这…

C++——关联式容器(2):AVL树(平衡二叉树)

2.AVL树 2.1 AVL树的概念 在学习了二叉搜索树后&#xff0c;我们发现了二叉搜索树可以根据大小比较来进行类似于折半查找的操作&#xff0c;使得搜索时间复杂度达到logn的水准。但是在面对极端情况下&#xff0c;如近似有序的序列&#xff0c;那么整棵树的时间复杂度就有可能退…

【Godot4.3】多边形的斜线填充效果基础实现

概述 图案&#xff08;Pattern&#xff09;填充是一个非常常见的效果。其中又以斜线填充最为简单。本篇就探讨在Godot4.3中如何使用Geometry2D和CanvasItem的绘图函数实现斜线填充效果。 基础思路 Geometry2D类提供了多边形和多边形以及多边形与折线的布尔运算。按照自然的思…

Spring-@Bean的处理流程

Bean前置知识 1 需要再Configuration Class中才能被解析 2 静态Bean也就是标注在static方法上的 实例Bean标注在普通方法上的 所有的Bean在创建之前都会变成BeanDefinition,其中有这样两个属性&#xff1a; setFactoryMethodName&#xff1a;静态方法 setFactoryBeanName&…