Android基础1-事件拦截机制分析
关于Android的事件拦截机制,其实就是针对1个MotionEvent.ACTION_DOWN, 多个MotionEvent.ACTION_MOVE, 1个MotionEvent.ACTION_UP,
这一个完整的操作引起的一系列传递处理:
- 基本传递流程
- Show you my code
- 总结
- 下章内容
基本传递流程
Android的事件传递机制可以分为2部分,可以理解为1个V字型的结构。
ONE:
由上往下的传递过程,这个过程主要看ViewGroup的dispatchTouchEvent(…)的返回值,要是
关联函数有:dispatchTouchEvent,requestDisallowInterceptTouchEvent,onInterceptHoverEvent
TWO
由下往上的处理过程,这个过程是因为当前所处的View已经是这个ViewGroup树的最后一个节点了,
当前的View就是事件处理过程的起点,传递过程的终点。
这里的概念是:
要是当前的起点把dispatchTouchEvent(…)返回了true就表示把这个动作吃了,回调给上一级的父ViewGroup的时候父ViewGroup就没有处理的机会了。
要是当前的起点把dispatchTouchEvent(…)返回了false就表示并没有吃掉这个动作,回调给上一级的父ViewGroup的时候父ViewGroup的时候,交由父ViewGroup去处理。一直往上上去,一旦true就不交给父ViewGroup处理。
Show you my code
package com.cai.boluo.test;
import android.view.MotionEvent;
import android.view.View;
public class ViewGroup {
protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
protected int mGroupFlags;
public boolean dispatchTouchEvent() {
boolean handled = false;
boolean isIntercepted;
if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0) {
isIntercepted = false;
} else {
isIntercepted = onInterceptTouchEvent();
}
if (!isIntercepted) {
for (int i = 0; i < childCount; i++) {
View ChildView = ViewChild[i];
handled = ChildView.dispatchTouchEvent();
if (handled)
return true;
}
}
return onTouchEvent();
}
public boolean onInterceptTouchEvent() {
return false;
}
}
public class View {
private boolean CLICKABLE = false;
private boolean LONG_CLICKABLE = false;
public boolean dispatchTouchEvent() {
if (mOnTouchListener.onTouch()) {
return true;
}
if (onTouchEvent()) {
return true;
}
return false;
}
public boolean onTouchEvent() {
if (CLICKABLE || LONG_CLICKABLE) {
performClick();
performLongClick();
return true;
}
return false;
}
}
上面试我看了ViewGroup.java和View.java之后整理出来的个人理解。
Read the fucking source
ViewGroup.java:
可以看出,默认的onInterceptTouchEvent()是返回false的,表示默认ViewGroup是不会拦截事件传递由上往下传递下去的,而这里的
dispatchTouchEvent
可以看得出,不管是传递还是处理,入口一直都是dispatchTouchEvent。
FLAG_DISALLOW_INTERCEPT
这个值判断优先级高于onInterceptTouchEvent,也就是说,如果针对ViewGroup的requestDisallowInterceptTouchEvent()函数处理了FLAG_DISALLOW_INTERCEPT这个值,那onInterceptTouchEvent写了也是白写。
当FLAG_DISALLOW_INTERCEPT和onInterceptTouchEvent的条件满足可以传递下去的时候,VIewGroup就会遍历自己子View,把事件传递下去。这里就是传递的过程。否则事件就不会传递下去,直接交由当前的ViewGroup自己执行处理流程,开始走由下往上的处理流程。
事件的处理流程
由代码可以看得出,
public boolean dispatchTouchEvent() {
if (mOnTouchListener.onTouch()) {
return true;
}
if (onTouchEvent()) {
return true;
}
return false;
}
public boolean onTouchEvent() {
if (CLICKABLE || LONG_CLICKABLE) {
performClick();
performLongClick();
return true;
}
return false;
}
事件的处理是这样子的,首先判断当前的mOnTouchListener.onTouch()的返回值,如果是true就直接返回了,可以看出mOnTouchListener.onTouch()的优先级远远高于普通的mOnClickListener.
如果View没有设置mOnTouchListener,就会去走onTouchEvent(),在onTouchEvent()里面才会判断是否是* CLICKABLE || LONG_CLICKABLE*,有的话就分别执行我们平时设置的mOnclickListener和长按。
返回值: 取决于mOnTouchListener.onTouch()的返回值—–>onTouchEvent()的返回值—->onTouchEvent()执行了onClick和onLongClick之后会返回true,不能点击和长按的话才会返回false
总结
1:事件拦截机制分为传递和处理2个过程。
2:总入口都是ViewGroup和View的dispatchTouchEvent
3:由上往下,一旦为true不传递;由下往上,一旦为true不给处理
4:ViewGroup的拦截看FLAG_DISALLOW_INTERCEPT和onInterceptTouchEvent
5:View没有onInterceptTouchEvent不需要像ViewGroup一样去判断要不要传递下去,因为本来就是节点了,没法传递下去了,只需要看能不能在View里面把事件给处理掉。处理完了父的onTouchEvent还给不给执行。
6:View的处理优先级最高的是mOnTouchListener.onTouch(),返回true的话后面onTouchEvent()的click和longclick都没机会走了,返回false的话,继续看onTouchEvent()的CLICKABLE || LONG_CLICKABLE,一旦是CLICKABLE || LONG_CLICKABLE就会返回true,否则就会返回false.
7: View的dispatchTouchEvent()返回true的话,父ViewGroup就没机会执行自己的onTouchEvent(),要是返回false的话,父的onTouchEvent()还可以执行到。
Tips
处理View的滑动冲突
1:外部拦截法
所有的点击事件都是父ViewGroup中进行拦截,方式如下:
在onInterceptTouchEvent中根据event在DOWN, MOVE,UP 这3种情况下的坐标,来决定:
当前的ViewGroup中返回true,则事件交由当前ViewGroup的onTouchEvent来处理。
当前的ViewGroup中返回了false,则事件交给childView处理。
2:内部拦截法
所有的点击事件都交给子View去处理,父ViewGroup中的onInterceptTouchEvent一直返回false.
子View在dispatchTouchEvent中根据DOWN,MOVE,UP这3种情况的坐标,来调用parent.requestDisallowInterceptTouchEvent(true/false);
该函数可以在子View中要求父ViewGroup要不要拦截掉事件。