您的位置:首页 > 移动开发 > Android开发

Android事件分发机制---学习笔记

2016-07-27 14:59 435 查看

学习来源

图解 Android 事件分发机制

从源码角度分析android事件分发处理机制

android事件拦截处理机制详解

《Android开发艺术探索》

Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

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

学习到的知识

整个事件流向应该是从Activity—->ViewGroup—>View 从上往下调用dispatchTouchEvent方法

到View的时候,再由View—>ViewGroup—>Activity从下往上调用onTouchEvent方法。

android对事件分发的顺序为:Activity–>PhoneWindow->DecorView->yourView;

android控件对事件处理的优先级:onTouch>onTouchEvent>onClick

dispatchTouchEvent==》分发事件 | onInterceptTouchEvent==》拦截事件 | onTouchEvent==》处理事件

整个View的事件转发流程是:

View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。


Activity的dispatchTouchEvent(MotionEvent ev)

public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//该方法为空方法,直接忽略之
onUserInteraction();
}
//把事件ev交给phoneWindow来处理
if (getWindow().superDispatchTouchEvent(ev)) {
//表明整个事件到此结束,处理完毕
return true;
}
4000

//说明该事件在View中没有得到处理,由Activity自己处理
//至于怎么处理博客后面后有说明
return onTouchEvent(ev);
}


接着是
superDispatchTouchEvent
的方法

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}


mDecor.superDispatchTouchEvent
传给了DecorView

public boolean dispatchTouchEvent(event){
//如果当前View对此事件拦截成功
if(this.onInterceptTouchEvent(event)){
//由当前View对此事件进行处理
//true 表示处理了该事件,false表示没有处理该事件
return onTouchEvent(event);
}else{//没有拦截成功
//交给子类来分发拦截处理
return child.dispatchTouchEvent(event);
}
}


所以就有了这张图片



ViewGroup永远不会对拦截,因为他的onInterceptTouchEvent(MotionEvent ev)始终返回的是false!这样DecorView对到来的事件MotionEvent就只有分发到子View并由子View进行拦截和处理此事件了.

View包括直接继承于View的子类因为其父类View没有onInterceptTouchEvent方法,所以没法对事件进行拦截,如果这种View获取到了事件,那么就会执行onTouchEvent方法(当然这也是有条件的,这个前提条件在对下面onTouch方法作用的时候会有说明)。

View的dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event) {
if (!onFilterTouchEventForSecurity(event)) {
return false;
}
//从这里看出,onTouch方法是优先于onTouchEvnent方法的
//如果onTouch方法返回了true的话,那么onTouchEvent方法就没法执行
//相应的onClick方法也不会去执行
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
//如果这里返回true的话,那么也表明此次事件被处理了
return true;
}
//回调onTouchEvent方法
return onTouchEvent(event);
}


setOnTouchListener


public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
}


ViewGroup的dispatchTouchEvent

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;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
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.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
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))  {
mMotionTarget = child;
return true;
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
if (target == null) {
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 (!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)) {
}
mMotionTarget = null;
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
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);
}


onTouchEvent的源码===>Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP://抬起事件才会执行onClick
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
..........
if (!mHasPerformedLongPress) {
.........
if (!focusTaken) {
.........
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
//这也就说明了onTouchEvent优先于onClick执行,因为onClick方法就在onTouchEvent方法里执行
//验证了上面的结论
if (!post(mPerformClick)) {
performClick();
}
}
}

...........
}
break;
}
//此时返回了true,此View处理了该事件,事件不会再往下进行分发
return true;
}
return false;
}


onTouch
优先于
onTouchEvent
|
onTouchEvent
优先于
onClick
===>onClick在onTouchEvent里面执行


返回true dispatchTouchEvent方法会结束执行 然后onClick就不会执行了

//因为该方法返回true,所以dispatchTouchEvent方法会结束执行
//进而导致Butto
c672
n的onClick方法也没机会执行
btn.setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});

btn.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
doClick();

}
});


android事件拦截处理机制详解



onInterceptTouchEvent
负责对
touch事件
进行拦截,对于嵌套的view最先执行的是事件拦截方法的是
最外层的那个view的onInterceptTouchEvent
方法,然后依次执行
子视图的onInterceptTouchEvent
,然后在执行子视图的子视图的事件拦截方法(当然在这里假设所有嵌套视图的onInterceptTouchEvent都会得到执行,让每个视图的onInterceptTouchEvent返回false即可)。参照上图,所以onInterceptTouchEvent执行顺序就是A—>B—>C—>D.也就是由父视图到子视图传递。总之,事件拦截机制是由父视图开始发起对事件的拦截(出事了老子先上,儿子稍后)。参照上图当手指触摸事件时,父视图A首先发起对该起事件的拦截,如果A拦截失败,就交给它的子视图B进行拦截;如果B拦截失败就交给B的子视图C再进行拦截..直到某一子视图对该次事件拦截成功。

某一视图拦截事件成功与否的
判断标识是onInterceptTouchEvent方法的返回值
,当返回
true
的时候说明拦截
成功
,返回
false
的时候说明当前视图对事件
拦截失败


下面说说拦截成功的情况,假设
C视图对当前touch事件拦截成功
。拦截成功意味着此次事件
不会再传递到D视图
了。所以此时的
D视图的onInterceptTouchEvent就得不到运行
(事件没法到达了,还拦截谁呢?)。事件拦截成功后,紧接着就会对事件进行处理,处理的方法教给onTouchEvent方法处理。此时C视图拦截成功,那么紧接着就会执行C视图的onTouchEvent方法,这是不是就意味着当前touch事件是由C视图的onTouchEvent方法来处理的呢?这要由C视图的onTouchEvent方法的返回值来决定。当C视图的onTouchEvent返回true的时候,当前事件就由C全权处理,处理的当然是事件的各种action,什么MotionEvent.ACTION_MOVE,ACTION_UP都交给了C的onTouchEvent方法进行处理。所以此时就可以在C的onTouchEvent方法中进行switch(event.getAction)判断执行相关逻辑了。如果返回的false,说明C视图对此事件不做处理或者处理不了,怎么办呢?儿子不行老爸来,于是事件就交到了B视图的onTouchEvent方法中。同样B对此事件处理与否还是看B的onTouchEvent返回值,具体的解释就跟C一样了,不复多言。

在A B C D的onInterceptTouchEvent和onTouchEvent都返回false的情况下,方法执行的顺序依次为A.onInterceptTouchEvent–>B.onInterceptTouchEvent–>C.onInterceptTouchEvent–>D.touchEvent(最深的子视图没重写onInterceptTouchEvent)–>C.touchEvent–>B.touchEvent–>A.touchEvent.也就是说拦截事件是父视图优先有子视图进行拦截,处理事件是子视图优先父视图进行处理。

onInterceptTouchEvent负责对事件进行拦截,拦截成功后交给最先遇到onTouchEvent返回true的那个view进行处理。

Android中touch事件的传递,绝对是先传递到ViewGroup,再传递到View的



当你点击了某个控件,
首先
会去调用该控件
所在布局的dispatchTouchEvent方法
,然后在布局的dispatchTouchEvent方法中
找到被点击的相应控件
,再去
调用该控件的dispatchTouchEvent方法
。如果我们点击了MyLayout中的按钮,会先去调用MyLayout的dispatchTouchEvent方法,可是你会发现MyLayout中并没有这个方法。那就再到它的父类LinearLayout中找一找,发现也没有这个方法。那只好继续再找LinearLayout的父类ViewGroup,你终于在ViewGroup中看到了这个方法,按钮的dispatchTouchEvent方法就是在这里调用的

在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。

子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。

重点推荐这张图来自==》图解 Android 事件分发机制



以下内容来自图解 Android 事件分发机制

dispatchTouchEvent
onTouchEvent
一旦return
true
,事件就停止传递了

dispatchTouchEvent
onTouchEvent
return
false
的时候事件都回传给父控件的onTouchEvent处理。

对于dispatchTouchEvent 返回 false 的含义应该是:事件停止往子View传递和分发同时开始往父控件回溯(父控件的onTouchEvent开始从下往上回传直到某个onTouchEvent return true),事件分发机制就像递归,return false 的意义就是递归停止然后开始回溯。

对于onTouchEvent return false 就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。

dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent

ViewGroup 和View的这些方法的默认实现就是会让整个事件安装U型完整走完,所以 return super.xxxxxx() 就会让事件依照U型的方向的完整走完整个事件流动路径),中间不做任何改动,不回溯、不终止,每个环节都走到。如果看到方法return super.xxxxx() 那么事件的下一个流向就是走U型下一个目标



onInterceptTouchEvent 的作用

默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent拦截器return super.onInterceptTouchEvent()和return false是一样的,是不会拦截的,事件会继续往子View的dispatchTouchEvent传递。

对于dispatchTouchEvent,onTouchEvent,return true是终结事件传递。return false 是回溯到父View的onTouchEvent方法。

ViewGroup 想把自己分发给自己的onTouchEvent,需要拦截器onInterceptTouchEvent方法return true 把事件拦截下来。

ViewGroup的拦截器onInterceptTouchEvent 默认是不拦截的,所以return super.onInterceptTouchEvent()=return false;

View 没有拦截器,为了让View可以把事件分发给自己的onTouchEvent,View的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent。

ViewGroup和View 的dispatchTouchEvent 是做事件分发,那么这个事件可能分发出去的四个目标

注:------> 后面代表事件目标需要怎么做。
1、 自己消费,终结传递。------->return true ;
2、 给自己的onTouchEvent处理-------> 调用super.dispatchTouchEvent()系统默认会去调用 onInterceptTouchEvent,在onInterceptTouchEvent return true就会去把事件分给自己的onTouchEvent处理。
3、 传给子View------>调用super.dispatchTouchEvent()默认实现会去调用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就会把事件传给子类。
4、 不传给子View,事件终止往下传递,事件开始回溯,从父View的onTouchEvent开始事件从下到上回归执行每个控件的onTouchEvent------->return false;
注: 由于View没有子View所以不需要onInterceptTouchEvent 来控件是否把事件传递给子View还是拦截,所以View的事件分发调用super.dispatchTouchEvent()的时候默认把事件传给自己的onTouchEvent处理(相当于拦截),对比ViewGroup的dispatchTouchEvent 事件分发,View的事件分发没有上面提到的4个目标的第3点。


ViewGroup和View的onTouchEvent方法是做事件处理的,那么这个事件只能有两个处理方式:

1、自己消费掉,事件终结,不再传给谁----->return true;
2、继续从下往上传,不消费事件,让父View也能收到到这个事件----->return false;View的默认实现是不消费的。所以super==false。


ViewGroup的onInterceptTouchEvent方法对于事件有两种情况:

1、拦截下来,给自己的onTouchEvent处理--->return true;
2、不拦截,把事件往下传给子View---->return false,ViewGroup默认是不拦截的,所以super==false;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: