[置顶] 一图详解Android View的事件分发机制
2017-10-25 18:17
781 查看
转载请注明出处:http://blog.csdn.net/haoyuegongzi/article/details/78344600
研究了许久的Android View点击事件的分发机制,到今天稍微有了一点头绪,写此博文,小作总结。
本文参考了郭霖的
Android事件分发机制完全解析,带你从源码的角度彻底理解(上):
http://blog.csdn.net/guolin_blog/article/details/9097463、
Android事件分发机制完全解析,带你从源码的角度彻底理解(下):
http://blog.csdn.net/guolin_blog/article/details/9153761、
以及任玉刚的《Android开发艺术探索》相关内容。
一、关于点击事件中TouchListener与ClickListener的优先级的问题:
xml布局如下:
Activity的代码如下:
OnTouchListener监听下onTouch方法返回false的执行情况:
OnTouchListener监听下onTouch方法返回true的执行情况:
可见:
1、View的TouchListener事件的优先级高于OnClick事件。当View的TouchListener事件的onTouch() 方法返回true时,表示View在TouchListener事件中消费了该事件,不在向下传递,OnClick事件不会执行。返回false时,表示View在TouchListener事件中不拦截该点击事件,这时OnClick事件才被执行。
2、TouchListener事件中,MotionEvent的状态不同,取值不同:
3、只要触摸到任何一个控件,就一定会调用该控件的dispatchTouchEvent方法,它被写在View.class里面,所有的控件以及layout布局都继承自View。因此点击任何级别的(子)View,其父类View的dispatchTouchEvent方法都会被调用执行,关于这一点,这里先提出来,下面我们接着分析。
二、View的分发机制:
View中dispatchTouchEvent方法的源码如下:
当if判断语句的三个条件同时为true的时候,返回true。
1、我们先看第一个mOnTouchListener:
在此,我们可以看出只要我们给控件注册了touch事件,也就是调用了mBtEvent.setOnTouchListener(new View.OnTouchListener() {……}方法,mOnTouchListener就一定被赋值了,也就是执行了:
那么mOnTouchListener 就一定不会为空,上面if判断的第一个条件就为真。
2、第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的View是否是enable可点击的,按钮默认都是enable可点击的,因此这个条件默认均为true。
3、第三个条件mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册Touch事件时的onTouch方法。
当三个条件同时满足,dispatchTouchEvent方法就返回true,表示该(子)View消费了这个点击事件,点击事件就不再向下传,当然OnClick事件也就不再被响应。在默认情况下,我们都没有重写dispatchTouchEvent方法以返回true或者注册Touch事件,那么mOnTouchListener 就为空,上面if判断语句中三个条件的第一个条件就为false,直接跳出if判断语句。因此dispatchTouchEvent方法默认返回false。
当三个条件任一个不满足的时候,dispatchTouchEvent方法就执行return onTouchEvent(event)方法,而在onTouchEvent(event)方法源码内部,我们发现onTouchEvent(event)方法处理了MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE、MotionEvent.ACTION_UP及MotionEvent.ACTION_CANCEL四个操作手势:
A、MotionEvent.ACTION_DOWN:主要是获取了event的坐标,用于判断当前event事件与View的关系。
B、MotionEvent.ACTION_MOVE:设置了MotionEvent.ACTION_DOWN事件发生后,按钮的背景变化,长按回调处理等。
C、MotionEvent.ACTION_UP:做了很多的处理,这里我们主要关心与Touch和Click相关的事情,仔细阅读源码我们发现在这里,他调用了performClick()方法,而performClick()方法恰恰是处理Click事件的:
mOnClickListener.onClick(this)是不是很熟悉?
有木有?
有木有?
有木有?
他的源码如下:
这正是我们惯用的写法——给按钮注册点击事件并复写他的onClick()方法:
MotionEvent.ACTION_CANCEL手势我们不做关注。
无论是返回onTouchEvent()还是调用performClick()方法处理onClick点击事件,都是在dispatchTouchEvent方法返回false的情况下发生的,这也解释了本文开篇出的结论:
View的TouchListener事件的优先级高于OnClick事件。当View的TouchListener事件的onTouch()方法返回true时,OnClick事件不会执行。返回false时,OnClick事件才被执行。
讲了这么多,现在终于对点击事件的内部执行情况有了大致的了解,那么在点击事件发生时,他又是按照怎样的一个脉络来执行的?
关于这个,我想用一幅图来表示:
下图中的根布局rlTootLayout、布局llSuperLayout、布局rlEvent、布局llEvent、按钮btEvent为本文开篇xml布局中的id,一 一对应,很方便查找
研究了许久的Android View点击事件的分发机制,到今天稍微有了一点头绪,写此博文,小作总结。
本文参考了郭霖的
Android事件分发机制完全解析,带你从源码的角度彻底理解(上):
http://blog.csdn.net/guolin_blog/article/details/9097463、
Android事件分发机制完全解析,带你从源码的角度彻底理解(下):
http://blog.csdn.net/guolin_blog/article/details/9153761、
以及任玉刚的《Android开发艺术探索》相关内容。
一、关于点击事件中TouchListener与ClickListener的优先级的问题:
xml布局如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/rlTootLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="bonc.demopractice_allkinsoff.view.DetecteViewActivity"> <LinearLayout android:id="@+id/llSuperLayout" android:layout_width="320dp" android:layout_height="match_parent" android:layout_centerInParent="true" android:orientation="vertical" android:background="#93a5f4"> <RelativeLayout android:id="@+id/rlEvent" android:layout_width="240dp" android:layout_height="260dp" android:layout_gravity="center_horizontal" android:layout_marginTop="30dp" android:background="#ffffff" android:visibility="visible"> <LinearLayout android:id="@+id/llEvent" android:layout_width="160dp" android:layout_height="160dp" android:orientation="vertical" android:layout_centerInParent="true" android:background="#93a5f4"> <Button android:id="@+id/btEvent" android:layout_width="120dp" android:layout_height="90dp" android:text="测试View的事件分发机制" android:layout_gravity="center" android:textAllCaps="false" android:layout_marginTop="5dp"/> </LinearLayout> </RelativeLayout> </LinearLayout> </RelativeLayout>
Activity的代码如下:
mBtEvent.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { Log.i("TAG", "onClick: 执行了onClick"); } }); mBtEvent.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("TAG", "onTouch: 执行了onTouch———>>ACTION_DOWN:" + event.getAction()); break; case MotionEvent.ACTION_MOVE: Log.i("TAG", "onTouch: 执行了onTouch———>>ACTION_MOVE:"+ event.getAction()); break; case MotionEvent.ACTION_UP: Log.i("TAG", "onTouch: 执行了onTouch———>>ACTION_UP:"+ event.getAction()); break; } return false; } });
OnTouchListener监听下onTouch方法返回false的执行情况:
onTouch: 执行了onTouch————>>>>ACTION_DOWN:0 onTouch: 执行了onTouch————>>>>ACTION_MOVE:2 onTouch: 执行了onTouch————>>>>ACTION_UP:1 onClick: 执行了onClick
OnTouchListener监听下onTouch方法返回true的执行情况:
onTouch: 执行了onTouch————>>>>ACTION_DOWN:0 onTouch: 执行了onTouch————>>>>ACTION_MOVE:2 onTouch: 执行了onTouch————>>>>ACTION_UP:1
可见:
1、View的TouchListener事件的优先级高于OnClick事件。当View的TouchListener事件的onTouch() 方法返回true时,表示View在TouchListener事件中消费了该事件,不在向下传递,OnClick事件不会执行。返回false时,表示View在TouchListener事件中不拦截该点击事件,这时OnClick事件才被执行。
2、TouchListener事件中,MotionEvent的状态不同,取值不同:
MotionEvent.ACTION_DOWN——>>event.getAction():0; MotionEvent.ACTION_MOVE——>>event.getAction():2; MotionEvent.ACTION_UP——>>event.getAction():1;
3、只要触摸到任何一个控件,就一定会调用该控件的dispatchTouchEvent方法,它被写在View.class里面,所有的控件以及layout布局都继承自View。因此点击任何级别的(子)View,其父类View的dispatchTouchEvent方法都会被调用执行,关于这一点,这里先提出来,下面我们接着分析。
二、View的分发机制:
View中dispatchTouchEvent方法的源码如下:
publicboolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }
当if判断语句的三个条件同时为true的时候,返回true。
1、我们先看第一个mOnTouchListener:
public void setOnTouchListener(OnTouchListener listener) { getListenerInfo().mOnTouchListener = listener; }
在此,我们可以看出只要我们给控件注册了touch事件,也就是调用了mBtEvent.setOnTouchListener(new View.OnTouchListener() {……}方法,mOnTouchListener就一定被赋值了,也就是执行了:
mOnTouchListener = listener;
那么mOnTouchListener 就一定不会为空,上面if判断的第一个条件就为真。
2、第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的View是否是enable可点击的,按钮默认都是enable可点击的,因此这个条件默认均为true。
3、第三个条件mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册Touch事件时的onTouch方法。
当三个条件同时满足,dispatchTouchEvent方法就返回true,表示该(子)View消费了这个点击事件,点击事件就不再向下传,当然OnClick事件也就不再被响应。在默认情况下,我们都没有重写dispatchTouchEvent方法以返回true或者注册Touch事件,那么mOnTouchListener 就为空,上面if判断语句中三个条件的第一个条件就为false,直接跳出if判断语句。因此dispatchTouchEvent方法默认返回false。
当三个条件任一个不满足的时候,dispatchTouchEvent方法就执行return onTouchEvent(event)方法,而在onTouchEvent(event)方法源码内部,我们发现onTouchEvent(event)方法处理了MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE、MotionEvent.ACTION_UP及MotionEvent.ACTION_CANCEL四个操作手势:
A、MotionEvent.ACTION_DOWN:主要是获取了event的坐标,用于判断当前event事件与View的关系。
B、MotionEvent.ACTION_MOVE:设置了MotionEvent.ACTION_DOWN事件发生后,按钮的背景变化,长按回调处理等。
C、MotionEvent.ACTION_UP:做了很多的处理,这里我们主要关心与Touch和Click相关的事情,仔细阅读源码我们发现在这里,他调用了performClick()方法,而performClick()方法恰恰是处理Click事件的:
public boolean performClick() { final boolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); return result; }
mOnClickListener.onClick(this)是不是很熟悉?
有木有?
有木有?
有木有?
他的源码如下:
public interface OnClickListener { void onClick(View v); }
这正是我们惯用的写法——给按钮注册点击事件并复写他的onClick()方法:
mBtEvent.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { } }
MotionEvent.ACTION_CANCEL手势我们不做关注。
无论是返回onTouchEvent()还是调用performClick()方法处理onClick点击事件,都是在dispatchTouchEvent方法返回false的情况下发生的,这也解释了本文开篇出的结论:
View的TouchListener事件的优先级高于OnClick事件。当View的TouchListener事件的onTouch()方法返回true时,OnClick事件不会执行。返回false时,OnClick事件才被执行。
讲了这么多,现在终于对点击事件的内部执行情况有了大致的了解,那么在点击事件发生时,他又是按照怎样的一个脉络来执行的?
关于这个,我想用一幅图来表示:
下图中的根布局rlTootLayout、布局llSuperLayout、布局rlEvent、布局llEvent、按钮btEvent为本文开篇xml布局中的id,一 一对应,很方便查找
相关文章推荐
- [置顶] Android事件分发机制 详解攻略,您值得拥有
- Android View 事件分发机制源码详解(ViewGroup篇)
- Android ViewGroup 事件分发机制详解
- Android 事件分发机制详解(1)-View
- Android View 事件分发机制详解
- Android中view的onTouch&onClick事件分发机制详解
- Android事件分发机制详解(2)----分析ViewGruop的事件分发
- Android View 事件分发机制详解
- Android触摸事件分发处理机制详解与源码分析一(View篇)
- Android ViewGroup/View 事件分发机制详解
- Android View 事件分发机制源码详解(ViewGroup篇)
- Android View事件分发机制详解
- [置顶] android之View和ViewGroup事件分发机制分析(一)(View的事件分发机制)
- Android:View事件分发机制详解
- Android:ViewGroup事件分发机制详解
- Android中事件处理机制之——View的事件分发详解(一)
- 【Android View事件(二)】详解事件分发机制
- Android事件分发机制详解(1)----探究View的事件分发
- Android 读书笔记:View的事件分发机制 源码详解 ------《Android开发艺术探索》
- Android View 事件分发机制