触摸事件的传递机制:

 

首先是最外层的viewgroup接收到事件,然后调用会调用自己的dispatchTouchEvent方法。如果在ACTION_DOWN的时候dispatchTouchEvent返回false则后续的ACTION_MOVE和ACTION_UP都接收不到了,如果在ACTION_DOWN的时候dispatchTouchEvent返回true,则在后续的动作都可以继续分发下去;

dispatchTouchEvent方法的调用过程中先会经过onInterceptTouchEvent方法判断,如果onInterceptTouchEvent返回true则会拦截,最终传递给本viewgroup的onTouchEvent方法;如果返回false,则不拦截,传递给子view/viewgroup的dispatchTouchEvent;而这个传递给子view/viewGroup的过程是这样的:

先会遍历所有的直属子view/ViewGroup ,看看点击事件是发生在哪个直属子view/ViewGroup上,确定之后,将事件传递给对应的直属直属子view/ViewGroup,直属子view/ViewGroup再调用自己的dispatchTouchEvent进行分发。。。这样循环,直到某一级ViewGroup的onInterceptTouchEvent进行拦截,onInterceptTouchEvent返回true传递给自己的onTouchEvent方法或者一直没有任何ViewGroup的onInterceptTouchEvent方法拦截事件,而事件传递到最终(最底层)的子view的onTouchEvent方法的时候,这时候如果onTouchEvent方法返回true,则会消费掉本次事件, 如果这时候onTouchEvent方法返回false。。,则事件会依次向上传递,先传递给自己的上一级的view/viewGroup的onTouchEvent方法,然后依次上传,直到某一级的onTouchEvent方法返回true,消费掉本次事件,或者没有任何一个onTouchEvent方法方法消费掉本次事件,最后事件在最上一层onTouchEvent方法返回false 之后消失掉。

onInterceptTouchEvent方法可以提供一个拦截能力,但是onInterceptTouchEvent方法只有在viewGroup里面才有,所以只有 viewGroup才有拦截事件的能力。

对于dispatchTouchEvent和onInterceptTouchEvent可以这样理解,dispatchTouchEvent方法是一个快递员,onInterceptTouchEvent方法是公司的门卫,快递员要给公司送的每批快递就是一个完整的触摸事件,每一批快递有一个为首的物品:Down事件;送货有一个规定:如果这批快递的为首的这个物品(Down)被门卫(onInterceptTouchEvent)给拦截了,那么这批快递之后的其他物品(Move,Up等)都不能通过门卫,而只有在第一个物品(Down)事件被门卫(onInterceptTouchEvent)放行的情况下,这批快递之后的其他物品才有可能投递成功。

一些要点:

1、Touch事件是由硬件捕获到触摸后由系统传递给应用的ViewRoot,再由ViewRoot往下一层一层传递.

2、处理过程都是自上而下的分发,可以看成是由布局的“包含”关系,自顶向下的方式

3、事件存在消耗,事件的处理方法都会返回一个boolean值,如果该值为true,则本次事件下发将会被消费掉,而不会继续往下一层传递.

4、Touch事件从ACTION_DOWN开始,也就是按下的这个action开始算起,到ACTION_UP抬起时结束;但是如果在ACTION_DOWN的时候,没有接受事件,那么后续的其他动作也将不会被接受

5、dispatchTouchEvent方法,是用来分发上层传递过来的事件的,它在View和ViewGroup中都有实现

6、onInterceptTouchEvent方法,是用来拦截事件传递的,它只在ViewGroup中有实现,在View中没有

7、view对象的TouchLitener中的onTouch方法总是会先于view自己的onTouchEvent(event)方法被执行,这是由View中的dispatchEvent方法决定。

8、Activity中的onTouchEvent只会在能响应的所有view都响应完事件,且都没有消费掉事件之后才会被调用。

9、如果一个ViewGroup被点击的地方,有多个子View/ViewGroup可以响应点击事件,那么它们响应的顺序是:后addView进来的子view/ViewGroup先响应事件或者是xml布局文件中后被添加的view先 响应触摸事件

 

触摸事件例子:

 

先来看一个简单的例子:这是一个底层布局为FrameLayout,其中又有一个RelativeLayout,RelativeLayout中又有一个TextView;这里说的“中”是指addView的关系。我们这里都使用自定义view的形式来实现,然后分别在MyFrameLayout,MyRelativeLayout和MyTextView中实现dispatchTouchEvent方法,并打印相关信息:

三层结构代码:

MyFrameLayout.java

package com.example.test.view.touch;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout; public class MyFrameLayout extends FrameLayout{ public MyFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} public MyFrameLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
} // 该方法用于分发上层传递过来的事件,它在View和ViewGroup中都有实现
@Override
public boolean dispatchTouchEvent(MotionEvent ev) { System.out.println("--MyFrameLayout-->dispatchTouchEvent-->start");
boolean b = super.dispatchTouchEvent(ev);
System.out.println("--MyFrameLayout-->dispatchTouchEvent-->end-->"+ev.getAction()+"-->"+b); return b;
} }

MyRelativeLayout.java

package com.example.test.view.touch;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout; public class MyRelativeLayout extends RelativeLayout { public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} public MyRelativeLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
} @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
System.out.println("--MyRelaativeLayout-->dispatchTouchEvent-->start");
boolean b = super.dispatchTouchEvent(ev); System.out.println("--MyRelaativeLayout-->dispatchTouchEvent-->end-->"+ev.getAction()+"-->"+b);
return b;
} }

MyTextView.java

package com.example.test.view.touch;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView; public class MyTextView extends TextView { public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} public MyTextView(Context context) {
super(context);
// TODO Auto-generated constructor stub
} @Override
public boolean dispatchTouchEvent(MotionEvent event) { System.out.println("--MyTextView-->dispatchTouchEvent-->start");
boolean b = super.dispatchTouchEvent(event); System.out.println("--MyTextView-->dispatchTouchEvent-->end-->"+event.getAction()+"-->"+b);
return b;
} }

MainActivity.java

package com.example.test;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout; import com.example.test.view.touch.MyFrameLayout;
import com.example.test.view.touch.MyRelativeLayout;
import com.example.test.view.touch.MyTextView; public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); /*
* When calling this method, the layout parameters of the specified view are ignored.
* Both the width and the height of the view are set by default to MATCH_PARENT.
* 如果直接调用此方法,View的LayoutParams 属性将被覆盖,无论是否设置了该属性,View的宽高将被替换成 MATCH_PARENT
*/
setContentView(initView()); } private View initView1() { RelativeLayout RelativeView = new RelativeLayout(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(300, 100);
RelativeView.setLayoutParams(layoutParams);
RelativeView.setBackgroundColor(Color.BLUE);
return RelativeView;
} // ios setupContentView
private View initView() { /* 初始化3个控件 */
// UIView *view = [[UIView alloc] init]
MyFrameLayout myFrameLayout = new MyFrameLayout(this);
MyRelativeLayout myRelativeLayout = new MyRelativeLayout(this);
MyTextView myTextView = new MyTextView(this); /* 分别设置LayoutParams */
// 设定当前Framelayout的宽高为200px*200px,其在320dpi的android设备上大小为100dip*100dip
FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(200, 200);
RelativeLayout.LayoutParams relativeLayoutParams = new RelativeLayout.LayoutParams(100, 100);
FrameLayout.LayoutParams frameLayoutParams1 = new FrameLayout.LayoutParams(100, 100); // 将RelativeLayout添加到FrameLayout中
myFrameLayout.addView(myRelativeLayout,frameLayoutParams);
// 将TextView添加到RelativeLayout中
myRelativeLayout.addView(myTextView, relativeLayoutParams); /* 设置Gravity居中 */
myRelativeLayout.setGravity(Gravity.CENTER);
frameLayoutParams.gravity = Gravity.CENTER;
frameLayoutParams1.gravity = Gravity.CENTER; /* 设置控件颜色 */
myFrameLayout.setBackgroundColor(Color.RED);
myRelativeLayout.setBackgroundColor(Color.GREEN);
myTextView.setBackgroundColor(Color.BLUE); // 将FrameLayout返回,作为Activity显示的View
return myFrameLayout;
} }

运行界面结构和UI关系如图:

红色为MyFrameLayout,绿色为MyRelativeLayout,蓝色为MyTextView

下面我们分别触摸红色(MyFrameLayout),绿色(MyRelativeLayout)和蓝色区域(MyTextView):

触摸红色(MyFrameLayout)打印信息:

触摸绿色(MyRelativeLayout)打印信息:

触摸蓝色区域(MyTextView)打印信息:

我们会发现三者的事件分发是包含关系:

MyTextView的dispatchToutchEvent方法是在MyRelativeLayout的dispatchToutchEvent方法调用的过程之中被执行完毕的,而MyRelativeLayout的dispatchToutchEvent方法是在MyFrameLayout的dispatchToutchEvent方法执行过程之中被执行完毕的。

下面我们将addView的方式改变一下,让MyTextView作为MyFrameLayout的直接子View,而不再是MyRelativeLayout的子view,而且让MyTextView在MyRelativeLayout之后被addView添加进MyFrameLayout中:

// 将RelativeLayout添加到FrameLayout中
myFrameLayout.addView(myRelativeLayout,frameLayoutParams);
// 将TextView添加到RelativeLayout中
myFrameLayout.addView(myTextView, frameLayoutParams1);

这时,界面UI关系如图:

然后我们再一次点击蓝色区域,打印的信息如下:

我们发现,这一次打印的信息与之前点击蓝色MyTextView区域时的打印信息不一样,MyTextView的dispatchTouchEvent方法并没有在MyRelativeLayout的dispatchTouchEvent方法内被调用,而是在MyFrameLayout的dispatchToutchEvent方法执行过程之中被执行完毕的;而且MyTextView的dispatchTouchEvent方法在MyRelativeLayout的dispatchTouchEvent方法开始之前执行就已经执行完毕了。

个人理解:

触摸事件会首先传递给Activity,然后Actvity再传递给DecorView,由于DecorView继承于FrameLayout,FrameLayout又继承于ViewGroup,所以会再依次调用FrameLayout及ViewGroup的dispatchTouchEvent方法,在ViewGroup的dispatchTouchEvent方法中会再次遍历子View/ViewGroup的dispatchTouchEvent方法,若其中的某个子控件在获取到事件之后返回结果为true,则该事件说明已被响应,本次分发过程结束;如果遍历过程结束,仍然没有控件响应触摸事件,则默认返回false,事件重新交由Activtiy的onTouchEvent方法来处理。

android自定义控件(9)-Android触摸事件分发机制的更多相关文章

  1. Android面试收集录6 事件分发机制

    转自:秋招面试宝典. 一. 基础认知 1.1 事件分发的对象是谁? 答:事件 当用户触摸屏幕时(View或ViewGroup派生的控件),将产生点击事件(Touch事件). Touch事件相关细节(发 ...

  2. Android为TV端助力 事件分发机制

    android事件分发机制,给控件设置ontouch监听事件,当ontouch返回true时,他就不会走onTouchEvent方法,要想走onTouchEvent方法只需要返回ontouch返回fa ...

  3. 高级UI晋升之触摸事件分发机制(一)

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680 0. 前言 鉴于安卓分发机制较为复杂,故分为多个层次进行讲解,分别为基础篇.实践 ...

  4. IOS 触摸事件分发机制详解

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者:MelonTeam 前言 很多时候大家都不关心IOS触摸事件的分发机制的实现原理,当遇到以下几种情形的时候你很可能抓破头皮都找不到解决方案 ...

  5. Android事件分发机制二:viewGroup与view对事件的处理

    前言 很高兴遇见你~ 在上一篇文章 Android事件分发机制一:事件是如何到达activity的? 中,我们讨论了触摸信息从屏幕产生到发送给具体 的view处理的整体流程,这里先来简单回顾一下: 触 ...

  6. cocos2dx 中触摸事件分发一些解读

    触摸事件分发中几个代码解读: 怎么说呢,感觉cocos2dx中的消息分发机制,相对于android中触摸事件分发机制要简单的多.因为android中要做区域判断,过滤器,以及父子组件分发给谁等等的逻辑 ...

  7. Android面试必问!View 事件分发机制,看这一篇就够了!

    在 Android 开发当中,View 的事件分发机制是一块很重要的知识.不仅在开发当中经常需要用到,面试的时候也经常被问到. 如果你在面试的时候,能把这块讲清楚,对于校招生或者实习生来说,算是一块不 ...

  8. Android事件分发机制浅谈(一)

    ---恢复内容开始--- 一.是什么 我们首先要了解什么是事件分发,通俗的讲就是,当一个触摸事件发生的时候,从一个窗口到一个视图,再到一个视图,直至被消费的过程. 二.做什么 在深入学习android ...

  9. Android View 事件分发机制 源码解析 (上)

    一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...

随机推荐

  1. ajax请求web服务返回json格式

    由于.net frameword3.5以上添加了对contenttype的检查,当ajax发送请求时,如果设置了contenttype为json,那么请求webservice时,会自动将返回的内容转为 ...

  2. javaweb项目打包成war包

    从来没有想过web项目还能打包的,但是有要求,就不得不去实现,在网上找了一下,发现挺简单的. 首先是使用MyEclipse将web项目打包,如下图所示. 右键选中项目,选择export. 然后选择J2 ...

  3. css-transition和transform实现图片悬浮移动动画

    今天在京东首页上看到一个效果,鼠标悬浮在图片上时,图片发生移动,鼠标移走时再移回,并且有一个过渡效果. 貌似很简单,自己做做试试吧 我首先使用的是jquery在鼠标悬浮到图片上给图片增加一个类,这个类 ...

  4. python-学习笔记1-面向对象编程

    1.面向对象最重要的概念就是类(Class)和实例(Instance),类是抽象的模板,而实例是根据类创建出来的一个个具体的"对象",每个对象都拥有相同的方法,但各自的数据可能不同 ...

  5. DFS经典题,reachable or not in a 2D maze

    [[0, 0, 0, 0, 0, 1], [1, 1, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0], [0, 1, 1, 0, 0, 1], [0, 1, 0, 0, 1, 0], ...

  6. 【bzoj2245】 SDOI2011—工作安排

    http://www.lydsy.com/JudgeOnline/problem.php?id=2245 (题目链接) 题意 n个产品,每个需要造C[i]件:m个员工,每个员工可以制造一些产品:每个员 ...

  7. Java学习路线图

    参考<疯狂Java实战演义>制作,觉得可以做一参考(放大网页看)

  8. scala break & continue

    Scala没有提供break和continue,我们可以自己实现一个,参考例子: import util.control.Breaks._ object BreakDemo { def main(ar ...

  9. 远程连接RabbitMQ失败

    远程连接RabbitMQ失败 为了避免污染宿主系统环境,于是在虚拟机中搭建了一个linux环境并且按照了rabbitmq-server.然后在远程连接的时候一直连接失败. 官网上面给的例子都是在本地使 ...

  10. AngularJs filter 过滤器

    Filter Ng里的过滤器. currency:把一个数字格式化成货币模式(如$1,234.56).当没有提供任何货币符号时,默认使用当前区域的符号. 使用: HTML:{{ currency_ex ...