Anroid View事件响应机制和ViewGroup的事件响应分发机制

注:低版本的源码内容比高版本的源码简单,分析起来方便,但是高版本源码更为严密。

View的事件响应机制

涉及2个方法dispatchTouchEvent和onTouchEvent

1.View的dispatchTouchEvent方法(事件传递到View,View的这个方法就自动执行。)

dispatchTouchEvent返回true,响应事件;返回false,不响应事件。


public boolean dispatchTouchEvent(MotionEvent event) {

...

ListenerInfo li = mListenerInfo;

if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {

return true;

}

if (onTouchEvent(event)) {

return true;

}    //等价于return onTouchEvent(event)

}

...

return false;       //(如果View没有setOnTouchListener,默认dispatchTouchEvent

事件就是返回false)

}

----------------------------------------------------------------------------------------------------------------------

上述源码中的ListenerInfo是View类中的一个静态成员类,里面封装了各种事件类型的监听

者XxxListener的变量,包括private OnTouchListener mOnTouchListener;

在View这个类中有ListenerInfo mListenerInfo;这个类型的成员变量

而mListenerInfo是通过下面这个方法来返回的,从方法可以看出返回值肯定不为空。

ListenerInfo getListenerInfo() {

if (mListenerInfo != null) {

return mListenerInfo;

}

mListenerInfo = new ListenerInfo();

return mListenerInfo;

}

结论1:所以View的dispatchTouchEvent方法中的if判断中li(也就是mListenerInfo)!=null

----------------------------------------------------------------------------------------------------------------------

再看View的setOnTouchListener这个方法,只要这个方法被调用了,参数不为空,那么

mListenerInfo.mOnTouchListener就不为空。

public void setOnTouchListener(OnTouchListener l) {

getListenerInfo().mOnTouchListener = l;

}

结论2:所以View的dispatchTouchEvent方法中的if判断中li.mOnTouchListener != null

所以


if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event))

这个条件组只是和li.mOnTouchListener.onTouch(this, event)这个回调方法有关

如果返回true的话,ImageView的dispatchTouchEvent方法也返回true。

如果返回false的话,会继续进行下面的一个if判断,也就是onTouchEvent方法的判断。


if (onTouchEvent(event)) {

return true;

}

上面这3行代码等价于return onTouchEvent(event),其实2.3.3的源码就是这么写的。

###############################################################################

所以对于dispatchTouchEvent()方法,如果直接继承自View的控件

1.没有调用setOnTouchListener()设置监听者,那么

li.mOnTouchListener == null,dispatchTouchEvent()方法默认就会返回false,这时

onTouchEvent 方法和dispatchTouchEvent()方法就没有任何的关联了。

2.调用setOnTouchListener()设置监听者,那么li.mOnTouchListener != null,

dispatchTouchEvent()方法就会判断li.mOnTouchListener.onTouch(this, event), 即监听者

的onTouch回调方法(表明用户的意图)的返回值

***onTouch 回调方法返回true,dispatchTouchEvent()方法就会返回true。

***onTouch 回调方法返回false,dispatchTouchEvent()方法就会调用onTouchEvent处理

事件,即把事件转交给onTouchEvent方法进行处理

###############################################################################

2.View的onTouchEvent方法

public boolean onTouchEvent(MotionEvent event) {

...

if (((viewFlags & CLICKABLE) == CLICKABLE ||

(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {

...

switch (event.getAction()) {

case MotionEvent.ACTION_UP:

...

if (!post(mPerformClick)) {

performClick();  /*

底层实现:li.mOnClickListener.onClick(this);

View的点击事件真正是在UP事件之后执行的。

*/

}

...

return true;

}

return false;

}

---------------------------------------------------------------------

View的onTouchEvent的返回值,取决于View是否可点击。

这就解释了为什么当ImageView和Button的监听事件的OnTouch事件都是返回false时,ImageView只能响应按下事件,而Button能响应所有事件的原因了:因为ImageView默认的clickable属性为false,而Button的clickable属性为true。

想让ImageView监听事件的onTouch返回false也能响应所有的事件,有2种方式

第1种:直接将ImageView的clickable属性设置为true。

第2种:为ImageView添加点击事件,通过查看下面的源码可以看出,点击事件会将View

的clickable属性设置为true。

public void setOnClickListener(OnClickListener l) {

if (!isClickable()) {

setClickable(true);

}

getListenerInfo().mOnClickListener = l;}

假如Button添加了点击事件,如果去屏蔽它的点击事件呢?

n 在添加点击事件的代码后面设置onclickable属性为false。

n 在onTouchListener监听的onTouch方法里返回true。

原理:因为View的点击事件的本质是由onTouchEvent方法中的,performClick

这个方法所执行的,不让View执行onTouchEvent或clickable为false

即不会执行点击事件。

由此也可以看出onTouch事件和onClick事件不是一回事

ViewGroup的事件响应和传递机制(以2.3.3的源码来分析)

涉及3个方法dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent

ViewGroup的事件传递,是伴随着递归算法查找坐标落在那一个控件的范围,由父控件

向子控件,由外到内。

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

...

final int action = ev.getAction();

final float xf = ev.getX();

final float yf = ev.getY();

final float scrolledXFloat = xf + mScrollX;

final float scrolledYFloat = yf + mScrollY;

final Rect frame = mTempRect;  //矩形类 mTempRect = new Rect();

boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;


if (action == MotionEvent.ACTION_DOWN) {

...

// If we‘re disallowing intercept or if we‘re allowing and we didn‘t intercept

//如果onInterceptTouchEvent(ev)没有拦截事件

//只有自己先不拦截,才有必要去判断有没有子View去响应这个事件。

if (disallowIntercept || !onInterceptTouchEvent(ev)) {

// reset this event‘s action (just to protect ourselves)

ev.setAction(MotionEvent.ACTION_DOWN);

// We know we want to dispatch the event down, find a child

// who can handle it, start with the front-most child.

final int scrolledXInt = (int) scrolledXFloat;

final int scrolledYInt = (int) scrolledYFloat;

final View[] children = mChildren;

final int count = mChildrenCount;

for (int i = count - 1; i >= 0; i--) {

final View child = children[i];

if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE

|| child.getAnimation() != null) {  //当child

可见或有动画时

child.getHitRect(frame);    //测量子控件的矩形参数

//判断事件的坐标有没有包含在子控件的矩形范围之内

if (frame.contains(scrolledXInt, scrolledYInt)) {

//计算事件在子View中的坐标

final float xc = scrolledXFloat - child.mLeft;

final float yc = scrolledYFloat - child.mTop;

ev.setLocation(xc, yc);

child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;

if (child.dispatchTouchEvent(ev))  {

// Event handled, we have a target now.!!!!

//子View如果处理了事件,就把这个View作为事件的目

mMotionTarget = child;

return true;

}

// The event didn‘t get handled, try the next view.

// Don‘t reset the event‘s location, it‘s not

// necessary here.

}

}

//子View能够成为mMotionTarget的前提是事件坐标落在它的

矩形范围之内,并且它响应处理了这个事件。

}

}

}  Action_down的if结束

...


// The event wasn‘t an ACTION_DOWN, dispatch it to our target if

we have one.(**没有子View响应**)

final View target = mMotionTarget;

if (target == null) {

// We don‘t have a target, this means we‘re handling the

// event as a regular view.(**如果没有子View响应这个事件,这个

事件就会当作一般的View的事件来处理,即ViewGroup执行像View

一样的去执行事件分发**)

ev.setLocation(xf, yf);

if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {

ev.setAction(MotionEvent.ACTION_CANCEL);

mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;

}

return super.dispatchTouchEvent(ev);

}


// if have a target, see if we‘re allowed to and want to intercept its events(**有子View响应**)

if (!disallowIntercept && onInterceptTouchEvent(ev)) {

final float xc = scrolledXFloat - (float) target.mLeft;

final float yc = scrolledYFloat - (float) target.mTop;

mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;

ev.setAction(MotionEvent.ACTION_CANCEL);

ev.setLocation(xc, yc);

if (!target.dispatchTouchEvent(ev)) {

// target didn‘t handle ACTION_CANCEL. not much we can do

// but they should have.

}

// clear the target

mMotionTarget = null;

// Don‘t dispatch this event to our own view, because we already

// saw it when intercepting; we just want to give the following

// event to the normal onTouchEvent().

return true;

}


// finally offset the event to the target‘s coordinate system and

// dispatch the event.(**把事件转交给子View去处理**)

final float xc = scrolledXFloat - (float) target.mLeft;

final float yc = scrolledYFloat - (float) target.mTop;

ev.setLocation(xc, yc);

if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {

ev.setAction(MotionEvent.ACTION_CANCEL);

target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;

mMotionTarget = null;

}

return target.dispatchTouchEvent(ev);

}

时间: 2024-10-24 17:15:18

Anroid View事件响应机制和ViewGroup的事件响应分发机制的相关文章

android的 View和 ViewGroup的事件分发机制

Android时间分发 View的时间分发过程dispatchTouchEvent —> onTouch –-> onTouchEvent /** * Pass the touch screen motion event down to the target view, or this * view if it is the target. * * @param event The motion event to be dispatched. * @return True if the eve

Android中的事件分发机制(下)——View的事件处理

综述 在上篇文章Android中的事件分发机制(上)--ViewGroup的事件分发中,对ViewGroup的事件分发进行了详细的分析.在文章的最后ViewGroup的dispatchTouchEvent方法调用dispatchTransformedTouchEvent方法成功将事件传递给ViewGroup的子View.并交由子View进行处理.那么现在就来分析一下子View接收到事件以后是如何处理的. View的事件处理 对于这里描述的View,它是ViewGroup的父类,并不包含任何的子元

Android查缺补漏(View篇)--事件分发机制源码分析

在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)--事件分发机制> ,先来看一下本篇的分析思路,一会儿会按照事件传递的顺序,针对以下几点进行源码分析: Activity对点击事件的分发过程 PhoneWindow是如何处理点击事件的 顶级View对点击事件的分发过程 View对点击事件的处理过程 Activity对点击事件的分发过程 通过上一篇博文中我们

图解 Android 事件分发机制

首发原文:http://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=2649548149&idx=1&sn=709149df682c7d3a6e453c9ef0626a1f&chksm=f1180e08c66f871eb2e7e39e057a5b090214fd71adcd98aa36b3d7fcecf77ad5d08138c50131#rd 在Android开发中,事件分发机制是一块Android比较重要的知识体系,了解并熟

android 事件分发机制(图文详解)

在Android开发中,事件分发机制是一块Android比较重要的知识体系,了解并熟悉整套的分发机制有助于更好的分析各种点击滑动失效问题,更好去扩展控件的事件功能和开发自定义控件,同时事件分发机制也是Android面试必问考点之一,如果你能把下面的一些事件分发图当场画出来肯定加分不少.废话不多说,总结一句:事件分发机制很重要. Android 事件分发流 关于Android 事件分发机制网上的博文很多,但是很多都是写个Demo然后贴一下输出的Log或者拿源码分析,然后一堆的注释和说明,如果用心的

[Android] View和ViewGroup事件分发机制

在android开发中会经常遇到滑动冲突(比如ScrollView或是SliddingMenu与ListView的嵌套)的问题,需要我们深入的了解android事件响应机制才能解决,事件响应机制已经是android开发者必不可少的知识. 1.涉及到事件响应的常用方法构成 用户在手指与屏幕接触过程中通过MotionEvent对象产生一系列事件,它有四种状态: MotionEvent.ACTION_DOWN :手指按下屏幕的瞬间(一切事件的开始) MotionEvent.ACTION_MOVE :手

Android6.0 ViewGroup/View 事件分发机制详解

之前自认为对于Android的事件分发机制还算比较了解,直到前一阵偶然跟人探讨该问题,才发现自己以前的理解有误,惭愧之余遂决定研习源码,彻底弄明白Android的事件分发机制,好了废话少说,直接开干. 首先,我们对Android中的touch事件做一下总结,主要分为以下几类: 1.Action_Down  用户手指触碰到屏幕的那一刻,会触发该事件: 2.Action_Move   在触碰到屏幕之后,手指开始在屏幕上滑动,会触发Action_Move事件: 3.Action_Up       在用

Android开发-分析ViewGroup、View的事件分发机制、结合职责链模式

介绍 上一篇博客职责链/责任链模式(Chain of Responsibility)分析理解和在Android的应用 介绍了职责链模式,作为理解View事件分发机制的基础. 套用职责链模式的结构分析,当我们的手指在屏幕上点击或者滑动,就是一个事件,每个显示在屏幕上的View或者ViewGroup就是职责对象,它们通过Android中视图层级组织关系,层层传递事件,直到有职责对象处理消耗事件,或者没有职责对象处理导致事件消失. 关键概念介绍 要理解有关View的事件分发,先要看几个关键概念 Mot

Android View框架总结(八)ViewGroup事件分发机制

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52298780 上篇分析了View的事件分发流程,留了一个问题:如果上面的EventButton继承TextView的话,按下抬起,会有一个现象,我可以告诉大家现象:就是只有dispatchTouchEvent ACTION_DOWN,onTouch ACTION_DOWN,onTouchEvent ACTION_DOWN这三个,你移动,或者抬起,是没有MOVE,或者