点击事件**;
点击事件**的**事件分发**,本质是对**MotionEvent事件**的**分发过程**,**
即,
当一个**MotionEvent**产生了以后,
系统需要把这个**事件**传递给一个**具体的View**,
而这个**传递的过程**就是**分发过程**。点击事件的分发过程**由三个重要方法共同完成:**dispatchTouchEvent**、**onInterceptTouchEvent**和**onTouchEvent**。**public boolean dispatchTouchEvent(MotionEvent ev)当前View**的**onTouchEvent**
和**下级View**的**dispatchTouchEvent**方法的影响;public boolean onInterceptTouchEvent(MotionEvent event)dispatchTouchEvent()内部调用,用来判断是否拦截某个事件;拦截**了某个事件,那么在**同一个事件序列**当中,
此方法不会**被再次调用**,是否拦截当前事件**。public boolean onTouchEvent(MotionEvent event)dispatchTouchEvent**方法中调用,用来**处理点击事件**;是否消耗当前事件**,不消耗**,则在**同一个事件序列**中,
当前View无法**再次接收**到事件。 public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}View层**的**传递规则**: 根ViewGroup**来说,
点击事件**产生**后,首先会**传递**给它,
这时其**dispatchTouchEvent**会被调用;onInterceptTouchEvent**方法
返回**true**就表示它**要拦截**当前事件,
接着事件就会交给这个**ViewGroup**处理,
即它的**onTouchEvent**方法就会被调用;!!!onInterceptTouchEvent**方法
返回**false**就表示它不拦截当前事件,
这时当前事件就会**继续传递**给它的**子元素**,
接着**子元素**的**dispatchTouchEvent**方法就会**被调用**,
如此反复直到事件被最终处理。onTouch()**-->**onTouchEvent()** -->**performClick()** --> onClick()**】!!!,**
否则继续往下传!这里可以看一下文末的两篇博客!

View**需要**处理事件**时,
如果它设置了**OnTouchListener**,
则**OnTouchListener**中的**onTouch**方法会被回调;onTouch**的**返回值**,- **如果返回****`false`****,【事件不消费,继续往下传递】** 【注意这里跟**onInterceptTouchEvent**不一样,
onInterceptTouchEvent**仅在ViewGroup级,**
true表拦截处理,调用**ViewGroup**自身的**onTouch()**-->**onTouchEvent()**,
onTouch**在View级时候,**
false**表**继续流程**,调用**View**自身的**onTouchEvent()**】**
- **如果返回****`true`****,【事件被消费】** 优先级:**
onTouch()**-->**onTouchEvent()** -->**performClick()** -->onClick()以上是事件处理方法的优先级顺序,按照这个顺序, 只要**排在前面**的**事件方法**返回**true**,**消耗处理**了**点击事件**了,点击事件**便就地结束,不再下发,**排在后面**的**点击事件**也就不会再被调用和响应了;** 【文末有实例】另,onTouch()的实现需要实现onTouchListener;onTouchEvent()/performClick()直接在自定义View文件中重写即可;onClick()的实现需要实现onClick;
Activity -> Window -> 顶级View**(上述说的表示View层中的顺序);

onTouchEvent**返回**false**,
那么它的父容器的**onTouchEvent**将会被调用,
依此类推。
【除非下往上回传到某个返回true的onTouchEvent(),
则在那里停止,否则——】Activity**处理,
即**Activity**的**onTouchEvent**方法会被调用。关于事件传递机制的一些结论(每一个点前面的短语是一个笔者自提的概况中心,便于记忆) 根据它们可以更好地理解整个传递机制: (1)【事件序列,定义】 “**
同一个事件序列**” 的定义: 指从手指**接触**屏幕的那一刻**起**, 到手指**离开**屏幕的那一刻**结束**, 在这个过程中**所产生**的一系列事件, 这个事件序列以**down事件**开始, 中间含有**数量不定**的**move**事件, 最终以**up事件**结束。

(2)【处理事件,独一无二】
正常情况下,**一个事件序列**只能被**一个View**拦截且**消耗**!!!
这一条的原因可以参考(3),
因为一旦一个元素**拦截**了某此事件,
那么**同一个事件序列内**的**所有事件**都会直接**交给它处理**!!!
因此**同一个事件序列中**的**事件**不能分别由两个**View**同时处理!!!
除非,
将本该由某个**View**自己处理的事件
通过**onTouchEvent**强行传递给**其他View**处理。
(3)【事件序列,从一而终】
某个**View**一旦决定**拦截**,则这一个**事件序列**都只能由它来处理
(如果事件序列能够传递给它的话),
并且它的**onInterceptTouchEvent**不会再被调用!!!
当一个**View**决定**拦截**一个事件后,
那么系统会把**同一个事件序列内**的**其他方法**都直接交给它来处理,
因此
就不用再调用这个View的**onInterceptTouchEvent**去询问它是否要拦截了。
(4)【短期失信】
某个**View**一旦**开始处理事件**,
如果它**不消耗ACTION_DOWN**事件(**onTouchEvent**返回了**false**),
那么**同一事件序列中**的**其他事件**都不会再交给它来处理,
【即,**View**放弃处理ACTION_DOWN,便放弃了**整个事件序列**!!!】
并且事件将重新交由它的**父元素**去处理,
即父元素的**onTouchEvent**会被调用。【事件向上“回传”】
即,
事件一旦交给一个View处理,那么它就必须消耗掉!!!
否则**同一事件序列**中剩下的事件就不再交给它来处理了!!!
好比上级交给程序员一件事,如果这件事没有处理好,
短期内上级就不敢再把事情交给这个程序员做。
(5)【余粮上缴】
如果View不消耗除ACTION_DOWN以外的其他事件,
那么这个点击事件会消失,
此时父元素的onTouchEvent并不会被调用,
并且当前View可以持续收到后续的事件,
最终这些**消失的点击事件**会传递给**Activity**处理。
(6)ViewGroup默认不拦截任何事件。
Android源码中
ViewGroup的**onInterceptTouch-Event**方法默认返回**false**。
(7)View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用。
(8)View的**onTouchEvent**默认都会**消耗事件**(返回**true**)!!!!!!!
除非它是**不可点击**的(**clickable** 和**longClickable**同时为**false**)。
View的**longClickable**属性默认都为**false**,
clickable**属性要分情况,**
比如**Button**的clickable属性默认为**true**,
而**TextView**的clickable属性默认为**false**。
(9)【**enable**无用,**clickable**居上】
View的**enable属性**不影响**onTouchEvent**的**默认返回值**。哪怕一个View是**disable**状态的!!!!!
只要它的**clickable**或者**longClickable**有一个为**true**,
那么它的**onTouchEvent**就返回true!!!
(10)**onClick**会发生的前提是**当前View**是可点击的,并且它收到了**down**和**up**的事件。
(11)【由外而内;以下犯上】
事件传递过程是**由外向内**的,
即事件总是先传递给**父元素**,然后再由**父元素**分发给**子View**,
通过**requestDisallowInterceptTouchEvent**方法可以在**子元素**中**干预父元素**的**事件分发**过程,但是**ACTION_DOWN**事件除外。
稍微复习一下: 事件方法的优先级:**
onTouch()**-->**onTouchEvent()** -->**performClick()** -->onClick()以上是事件处理方法的优先级顺序,按照这个顺序, 只要**排在前面**的**事件方法**返回**true**,**消耗处理**了**点击事件**了,点击事件**便就地结束,不再下发,**排在后面**的**点击事件**也就不会再被调用和响应了;** 下面是关于**事件优先级**的一个实例:
public class DragView3 extends View implements View.OnClickListener {
private int lastX;
private int lastY;
public DragView3(Context context) {
super(context);
ininView();
}
public DragView3(Context context, AttributeSet attrs) {
super(context, attrs);
ininView();
}
public DragView3(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ininView();
}
private void ininView() {
setBackgroundColor(Color.BLUE);
this.setOnClickListener(this);//测试onTouchEvent与onClick的优先级!!
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录触摸点坐标
lastX = (int) event.getX();
lastY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
// 计算偏移量
int offsetX = x - lastX;
int offsetY = y - lastY;
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
// LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
break;
}
return true;
}
//测试onTouchEvent与onClick的优先级!!
@Override
public void onClick(View v) {
setBackgroundColor(Color.RED);
}
}onClick监听器**,
如果**onClick**能**响应**,点击View之后会从**蓝色**变成**红色**,
但是运行之后我们发现并没有变色,即**onClick**没有被调用;
View响应的只是**onTouchEvent**中的**滑动逻辑**而已。(下面图一)onTouchEvent**返回**true**,把**事件消耗掉**了!!
于是事件在**onTouchEvent**中**处理结束**,不再往下传,传不到**onClick**那里!!!onTouchEvent**注释掉,
使之默认返回**false**,不消耗事件,这时**onClick**则会响应!
那么再次运行程序,可以发现点击View之后,
View从蓝色变成红色!!!(下面图二)事件处理方法**的**优先级**不言而喻!
图一

图二
dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent;分别的作用和关系;分发顺序**的**从上往下**的过程!!!!!
逻辑骨架就是,
接收到事件 --> 分发 --> 是否拦截
--> 拦截则就地处理【ViewGroup/View:调用自身**onTouch()**-->**onTouchEvent()** -->**performClick()** --> onClick()**】!!!,**
否则继续往下传,传到最下层的View为止,接着进入处理过程!
分发的顺序是**Activity -> Window(PhoneWindow) -> DecorView -> 顶级View(上述说的表示View层中的顺序) -> ViewGroup -> View**;
这里可以看一下文末的两篇博客! 
从下到上**的过程,
从**最下层的View**开始到**最上层**(即**Activity**),
如果所有元素都不消耗这个事件,事件最终就传回Activity;
消耗指onTouch、onTouchEvent、onClick等;Android事件分发流程: Activity -> ViewGroup -> View;Activity对点击事件的分发过程ViewGroup对点击事件的分发过程View对点击事件的分发过程点击事件**用**MotionEvent**来表示,**
当一个点击操作发生时,事件最先传递给当前Activity,
由Activity的dispatchTouchEvent来进行事件派发,
具体的工作是由**Activity内部**的**Window**来完成的!!!!!!!!Window**会将事件传递给**decor view**,**
decor view**一般就是当前界面的底层容器(即**setContentView**所设置的View的父容器),**
通过**Activity.getWindow.getDecorView()**可以获得。 public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}如上,
首先事件开始交给**Activity**所附属的**Window**进行**分发**,如果返回**true**,
整个事件循环就结束了:
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}返回**false**意味着事件没有元素处理,
所有View的**onTouchEvent**都返回了false,
那么Activity的onTouchEvent就会被调用。
return onTouchEvent(ev);Window**是个**抽象类**!!!**
而**Window**的**superDispatchTouchEvent**方法也是个**抽象方法**!!!
因此我们必须找到**Window的实现类**才行。源码:
public abstract boolean superDispatchTouchEvent(MotionEvent event);PhoneWindow**,
这一点从Window的源码中有这么一段话:Abstract base class for a top-level window look and behavior policy.
An instance of this class should be used as the top-level view added to
the window manager. It provides standard UI policies such as a background, title area,
default key processing, etc.
The only existing implementation of this abstract class is android. policy.
PhoneWindow,which you should instantiate when needing a Window.
Eventually that class will be refactored and a factory method added for creating
Window instances without knowing about a particular implementation.- **`Window类`****可以控制****`顶级View`****的****`外观`****和****`行为策略`****!!!**
- **它的****`唯一实现`****位于****`android.policy.PhoneWindow`****中!!!**
- **当你要****`实例化`****这个****`Window类`****的时候,** public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}PhoneWindow**将事件直接传递给了**DecorView**!!!!!!!!!!**DecorView**是什么:**private final class DecorView extends FrameLayout implements RootViewSurfaceTaker
// This is the top-level view of the window,containing the window decor.
private DecorView mDecor;
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}((ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content)).getChildAt(0)**可以获取**Activity**所设置的**View**!!!!!!!!
这个**mDecor**就是**getWindow().getDecorView()**返回的**View**!!!
而通过**setContentView**设置的View是它(DecorView mDecor)的一个**子View**【所谓**顶级View**】!!!DecorView**这儿,
由于**DecorView**继承自**FrameLayout**且是**父View**,
所以最终事件会传递给**View**!!!
从而**应用**能**响应点击事件**!!顶级View**了,
即
在**Activity**中通过**setContentView**所设置的**View**,
另外**顶级View**也叫**根View**,
顶级View**一般都是**ViewGroup**。**
点击事件**达到**顶级View**(一般是一个ViewGroup)以后,**
会调用**ViewGroup**的**dispatchTouchEvent**方法,
然后,
如果**顶级ViewGroup**拦截事件即**onInterceptTouchEvent**返回true,
则事件由ViewGroup处理,
如果ViewGroup的mOnTouchListener被设置则**onTouch**会被调用,
否则**onTouchEvent**会被调用。
如果都提供的话,onTouch会屏蔽掉onTouchEvent。子View**的**dispatchTouchEvent**会被调用。
到此,事件已经从顶级View传递给了下一层View,接下来的传递过程和顶级View是一致的,如此循环,完成整个事件的分发。以上是对原理部分的回顾; 下面开始顶级View的源码分析;
ViewGroup的dispatchTouch-Event方法中,
这个方法比较长,这里分段说明。首先下面一段,描述当前View是否拦截点击事情这个逻辑。
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}事件类型为ACTION_DOWN**或者**mFirstTouchTarget != null**。**
ACTION_DOWN事件好理解,那么mFirstTouchTarget != null是什么?子元素成功处理**时,
mFirstTouchTarget**会被赋值并**指向子元素**【于是 != null】,**
换种方式来说,
当ViewGroup【**不拦截事件**并将事件交**由子元素处理**时
mFirstTouchTarget != null**】。**
反过来,
一旦事件由当前ViewGroup**拦截**时,
mFirstTouchTarget != null**就不成立。**...
参考:
