草庐IT

ViewGroup事件分发源码分析

komine 2023-03-28 原文

1.AndroidStudio源码调试方式

AndroidStudio默认是支持一部分源码调试的,但是build.gradle(app) 中的sdk版本要保持一致,

最好是编译版本、运行版本以及手机的版本都保持一致,比如

android {
    compileSdkVersion 30  //1
    buildToolsVersion "30.0.0"

    defaultConfig {
        applicationId "komine.demos.app"
        minSdkVersion 26
	targetSdkVersion 30 //2
	versionCode 1
   	versionName "1.0"

	testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

设置断点的源码注意也要一致,比如设置的Android SDK 30,源码也要选择对应的版本.我的AndroidStudio版本是3.6.3

如果都一致发现断点的位置还是不正确,可以重置缓存,在 File-->Invalidate Caches 重新构建.

运行程序之后发现断点上有一个绿色的对勾,表示该断点也可以被执行,并不是100%,所以关键代码调试,最好步进调试.

如果断点成功执行了,如下图

2.ViewGroup事件分发源码分析

当我们在屏幕触摸的时候,不管你触摸到那里,是View上面,还是一个View的没有的Activity上,都会调用Activity的dispatchTouchEvent,如果要观察touchEvent事件分发过程,
可以自己定义一个ViewGroup,和View,在其中打印日志

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">

    <komine.demos.app.TouchLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <komine.demos.app.TouchView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="#39c5bb"/>

        <komine.demos.app.TouchView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="#39c5bb"/>

    </komine.demos.app.TouchLayout>

</LinearLayout>

所有Touch事件都会进到Activity.dispatchTouchEvent方法


public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        //如果是down事件,则会回调Activity的onUserInteraction()方法
        //我们可以在该方法中做一些其他事情
        onUserInteraction();
    }
    //getWindow()是mWindow,在Activity的attach方法中赋值,是一个PhoneWindow对象,是直接new PhoneWindow
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    //如果事件没有被消费,则会回调Activity的onTouchEvent()事件
    return onTouchEvent(ev);
}

PhoneWindow.superDispatchTouchEvent

如果你想知道mDecor是干什么的或者怎么被创建的,可以看我的另一篇博客
setContentView源码分析

我们直接把它当作ViewGroup看待就行了

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    //superDispatchTouchEvent() 其实是调用了ViewGroup的dispatchTouchEvent,
    //因为mDecor是继承自ViewGroup
    return mDecor.superDispatchTouchEvent(event);
}

重点来了,ViewGroup.dispatchTouchEvent

 @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    ...
    ...
    //函数的返回值,表示当前事件是否有View消费,true表示有View消费该事件
    //则其他View就不能再处理该事件了
    boolean handled = false;

    //View的方法,默认返回true
    if (onFilterTouchEventForSecurity(ev)) {
  	...
 	...
        //判断当前ViewGroup是否拦截该事件,
	final boolean intercepted;

        //down事件才判断是否拦截,所以ViewGroup要拦截事件,一定要在
        //down事件的时候拦截,其他事件会被忽略
	if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {

   	     //FLAG_DISALLOW_INTERCEPT 是在requestDisallowInterceptTouchEvent()中赋值的
             //表示子View是否请求了父ViewGroup不拦截本次事件,非ViewGroup子View可以调用getParent().requestDisallowInterceptTouchEvent()
             final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

	     //如果子View没有请求父ViewGroup不拦截自己
             if (!disallowIntercept) {
		    //调用当前ViewGroup的onInterceptTouchEvent()看看自己是否拦截
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action);
             } else {
                    intercepted = false;
             }
         } else {
              intercepted = true;
         }

	...
	...
	//caceled默认返回false, intercepted表示当前ViewGroup是否要拦截事件
	if (!canceled && !intercepted){
	    ...
	    ...
	    //这是一个倒序循环,最后的那个view会先进行事件分发
	    for (int i = childrenCount - 1; i >= 0; i--) {
		...
		...
		//见下方,dispatchTransformedTouchEvent()对子view进行事件分发
		if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
		    //如果有子View返回true,中断事件分发,break完成子View的事件分发
		}
	    }
	  
	}
    }
    //mFirstTouchTarget == null 说明没有子View消费当前事件,则会调用
    //父view的dispatchTouchEvent(),完成当前ViewGroup的事件分发
    //意思就是,我已经分完了,我的子View没人要消费事件,你看你要不要,把事件分发交给
    //父类去处理,然后父类又会重复上述步骤,进行事件分发
    if (mFirstTouchTarget == null) {
          handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
    } else {
	...
	handled = true;
    }
    //所有ViewGroup分发完成之后的最终结果
    return handled;
}

dispatchTransformedTouchEvent

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {
    final boolean handled;
    final int oldAction = event.getAction();

    ...
    ...
    //判断是否是同一根手指
    if (newPointerIdBits == oldPointerIdBits) {
	if (child == null || child.hasIdentityMatrix()) {
             if (child == null) {
		 //如果子View为空,则返回父类的dispatchTouchEvent()结果
                 handled = super.dispatchTouchEvent(event);
             } else {
		 ...
	         //调用子View的dispatchTouchEvent(),子View的dispatchTouchEvent()又会调用onTouchEvent(),
		 //如果View设置了TouchListener则会优先以TouchListener结果返回,然后才会以onTouchEvent()的结果
		 //作为返回值,这边不贴代码了,可以自己去看看
                 handled = child.dispatchTouchEvent(event);
		 ...
             }
             return handled;
        }
    }
}

Touch事件分发流程:

有关ViewGroup事件分发源码分析的更多相关文章

  1. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  2. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  3. ruby-on-rails - 事件记录 : Select max of limit - 2

    我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).

  4. ruby-on-rails - 事件管理员和自定义方法 - 2

    这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什

  5. ruby-on-rails - 在不重新查询数据库的情况下重新排序 Rails 中的事件记录? - 2

    例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果

  6. ruby-on-rails - Ruby 长时间运行的进程对队列事件使用react - 2

    我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby​​脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几

  7. ruby-on-rails - 使用 Rails 事件记录获取二级模型 - 2

    我有一个帖子属于城市的关系,城市又属于一个州,例如:classPost现在我想找到所有帖子及其所属的城市和州。我编写了以下查询来获取带有城市的帖子,但不知道如何在同一查找器中获取带有城市的相应州:@post=Post.find:all,:include=>[:city]感谢任何帮助。谢谢。 最佳答案 Post.all(:include=>{:city=>:state}) 关于ruby-on-rails-使用Rails事件记录获取二级模型,我们在StackOverflow上找到一个类似的问

  8. ruby - 在没有数据库的情况下伪造一个事件记录模型 - 2

    我觉得我错过了什么。我正在编写一个ruby​​gem,它允许与事件记录进行交互,作为其主要功能的附加功能。在为其编写测试用例时,我需要能够指定虚拟事件记录模型来测试此功能。如果我可以获得一个事件记录模型的实例,它不需要与数据库的任何连接,可以有关系,所有这些东西,但不需要我在数据库中设置表,那就太棒了。我对测试还很陌生,在Rails测试之外我也很陌生,但似乎我应该能够相当轻松地完成类似的事情,但我什么也没找到。谁能告诉我我错过了什么?我看过工厂、制造商、固定装置,所有这些似乎都想达到目标。人们如何在您只需要AR对象进行测试的地方测试gem? 最佳答案

  9. ruby-on-rails - 在事件记录库中添加某些方法的首选方法是什么? - 2

    我想创建一个模块,为从事件记录库继承的类提供一些通用方法。以下是我们可以实现的两种方式。1)moduleCommentabledefself.extended(base)base.class_evaldoincludeInstanceMethodsextendClassMethodsendendmoduleClassMethodsdeftest_commentable_classmethodputs'testclassmethod'endendmoduleInstanceMethodsdeftest_commentable_instance_methodputs'testinstanc

  10. 建模分析 | 平面2R机器人(二连杆)运动学与动力学建模(附Matlab仿真) - 2

    目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标

随机推荐