Android中事件处理机制之——ViewGroup的事件传递详解(一)
来源:互联网 发布:windows xp 32位 编辑:程序博客网 时间:2024/06/06 14:21
转载请注明出处:http://blog.csdn.net/xiaohao0724/article/details/54798908
通过对上篇 Android中事件处理机制之---View的事件分发详解(一) 的学习相信大家对Android事件处理机制都有了一定的了解。接下来今天我们继续来学习ViewGroup中的事件传递机制。
Android中的事件是从布局一层层向里面的布局或控件传递的,在传递过程中如果在哪层被拦(onInterceptTouchEvent返回true)则在这层被消费不会再向内层传递,如果传递到最内层事件没有完全响应将一层层向外传递,如果外层响应则消费掉事件,如果不响应将继续向外层传递直到最外层布局。
ViewGroup是根据onInterceptTouchEvent的返回值进行事件传递的,当返回false时向内层传递,返回true是拦截事件不再向内层传递
public boolean onInterceptTouchEvent(MotionEvent ev) { return false/true; }
我们先来新建一个工程EventViewGroup
自定义MyLinearlayout
public class MyLinearLayout extends LinearLayout {public MyLinearLayout(Context context) {super(context);}public MyLinearLayout(Context context, AttributeSet attrs) {super(context, attrs);}public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return false; // return super.onInterceptTouchEvent(ev);}}
activity_main.xml
<com.havorld.eventviewgroup.view.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/myLinearLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:background="@android:color/holo_green_light" android:gravity="center" android:padding="10dp" android:text="TextView" /> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:padding="10dp" android:text="Button" /></com.havorld.eventviewgroup.view.MyLinearLayout>
public class MainActivity extends Activity {protected static final String TAG = "TAG";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyLinearLayout myLinearLayout = (MyLinearLayout) findViewById(R.id.myLinearLayout);TextView textView = (TextView) findViewById(R.id.textView);Button button = (Button) findViewById(R.id.button);myLinearLayout.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {Log.e(TAG, "myLinearLayout---onTouch");return false;}});textView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.e(TAG, "textView---onClick");}});button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.e(TAG, "button---onClick");}});}}
当点击某个控件时,首先会去调用该控件所在布局父类ViewGroup中的dispatchTouchEvent方法,然后在该方法中遍历子控件找到被点击的对应控件,再去调用该控件的父类View中的dispatchTouchEvent方法。
上一篇Android中事件处理机制之---View的事件分发详解(一) 中我们已经对View中的dispatchTouchEvent进行过分析,现在我们来看一下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);}
一、对MyLinearLayout和TextView进行事件分析
分别点击TextView和空白处
1、onInterceptTouchEvent返回false
分析:
当点击了TextView时会先调用MyLinearLayout父控件ViewGroup中的dispatchTouchEvent,然后看源码的标记①处disallowIntercept || !onInterceptTouchEvent(ev),当两个条件有一个成立时进入判断中。disallowIntercept是指是否禁用掉事件拦截的功能,默认是false,也可以通过调用requestDisallowInterceptTouchEvent方法对这个值进行修改(不常用),第二个条件的值就是自定义MyLinearLayout复写onInterceptTouchEvent方法的返回值取反,此处我们返回的是false所以!onInterceptTouchEvent(ev) = true进入判断方法中,看标记②对布局中的子View进行遍历,在标记③处进行判断哪个View是所点击的View,在标记④处调用子View的事件分发机制child.dispatchTouchEvent(ev)并返回true执行TextView的onClick方法(注意:此处子View是TextView由于设置了onCick事件clickable 为真才返回true, 此处不明白的同学可参考上篇Android中事件处理机制之---View的事件分发详解(一) ),此时进入方法内在标记⑤处也就整个dispatchTouchEvent方法直接返回true后面的不再执行。
当点击空白处时先调用MyLinearLayout父控件ViewGroup中的onInterceptTouchEvent,在标记③处进行判断没有子View被点击,因为点击的是父布局,所以跳出判断继续向下执行到标记⑥处,由于没有运行标记④处所以没有跟mMotionTarget赋值,此时target = mMotionTarget = null进入判断方法中,在MyLinearLayout父控件ViewGroup内标记⑦处调用super.dispatchTouchEvent(ev)也就是ViewGroup的父控件View的事件分发机制(具体可参考Android中事件处理机制之---View的事件分发详解(一) ) 所以执行MyLinearLayout的onTouch方法。
2、onInterceptTouchEvent返回true
@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return true;// return super.onInterceptTouchEvent(ev);}
分析:
因为onInterceptTouchEvent返回true所以不管点击TextView还是MyLinearLayout在标记①处的判断都为false,所以不进入判断方法内,在标记⑥处进行判断进入方法内在标记⑦处把事件分发给MyLinearLayout父控件ViewGroup内标记⑦处调用super.dispatchTouchEvent(ev)也就是ViewGroup的父控件View,所以两次点击都执行MyLinearLayout的onTouch方法。
下面我们来做个拓展:
将TextView的onClick事件代码替换成onTouch事件(onInterceptTouchEvent返回false)
textView.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {Log.e(TAG, "textView---onTouch---" + event.getAction());return false;}});
分析:
点击TextView调用MyLinearLayout父控件ViewGroup中的dispatchTouchEvent,标记① 处 !onInterceptTouchEvent(ev) = true进入方法中,在标记④处调用的child.dispatchTouchEvent(ev)正是TextView的事件分发处理,根据上篇我们对事件分发的学习不难得出此处会只调用一次onTouch事件TextView执行按下事件并且child.dispatchTouchEvent(ev)的返回值为false,所以不会执行到标记⑤处返回而是直接执行到标记⑦处调用MyLinearLayout的父控件ViewGroup的super.dispatchTouchEvent(ev)事件分发,所以会执行MyLinearLayout的触摸事件。
这是父类布局和子控件结合在一块时的情况,各种组合多种多样但万变不离其宗,比如
当TextView的onTouch事件返回true时(onInterceptTouchEvent返回false)点击TextView:
当MyLinearLayout的触摸事件返回true时(T(onInterceptTouchEvent返回false,textView的onTouch返回false)点击一次TextView:
添加Activity的触摸事件
@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.e(TAG, "activity---onTouch");return super.onTouchEvent(event);}当MyLinearLayout和textView的触摸事件都返回false时(T(onInterceptTouchEvent返回false)点击一次TextView
总结一下:
二、对MyLinearLayout和Button进行事件分析
- Android中事件处理机制之——ViewGroup的事件传递详解(一)
- Android中事件处理机制之——View的事件分发详解(一)
- Android ViewGroup事件传递机制
- ViewGroup的事件传递机制
- Android事件处理(一)——ViewGroup的dispatchTouchEvent 函数源码详解
- Android 触摸事件机制(四) ViewGroup中触摸事件详解
- Android 触摸事件机制ViewGroup中触摸事件详解
- Android 之 ViewGroup 的事件分发机制
- ViewGroup事件传递机制
- ViewGroup事件传递机制
- Android ViewGroup中事件触发和传递机制
- Android ViewGroup中事件触发和传递机制
- Android ViewGroup中事件触发和传递机制
- Android Activity 和 ViewGroup中事件触发和传递机制
- Android ViewGroup中事件触发和传递机制
- Android Activity 和 ViewGroup中事件触发和传递机制
- Android ViewGroup中事件触发和传递机制
- Android ViewGroup中事件触发和传递机制
- 数据结构颓废计划III-基础线段树
- Qt入门之基础篇 ( 一 ) :Qt4及Qt5的下载与安装
- leetcode - 33. Search in Rotated Sorted Array
- 正则表达式——位置匹配
- SDL 显示bmp图片
- Android中事件处理机制之——ViewGroup的事件传递详解(一)
- 小明的烦恼
- ioremap_nocache() 函数的使用
- Qt入门之基础篇 ( 二 ) :Qt项目建立、编译、运行和发布过程解析
- 开源项目Meizhi学习笔记--RatioImageView
- 剑指offer——字符串的排列
- Duilib中将GDI换成GDI+
- 如何判断链表是否有环
- VS项目创建和Dll相关项目的注意事项