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和
onTouchEventreturn
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;
相关文章推荐
- Android从asset中获取drawable
- Android 百万级视频应用开发(二)
- 安卓版本更新的简单Demo
- Android:adb 启动activity、service,发送broadcast
- Android碎碎念 -- 组件化、模块化、插件化
- android定时器
- Android(java方法)上实现mp4的分割和拼接 (一)
- 获取drawable目录下图片的uri地址
- Android中的dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()
- Android(java方法)上实现mp4的分割和拼接 (二)
- tablayout
- Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路
- Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路
- 深入剖析Android音频之AudioTrack
- Android View绘制流程
- xmlpullparser之skip(parser)
- Android Studio实现代码混淆
- Android中Calendar类的用法总结
- android下大文件分割上传
- Android 5 消息机制源码分析