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>


MainActivity.java

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);}

一、对MyLinearLayoutTextView进行事件分析

分别点击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


分析:

点击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进行事件分析

需要注意的是默认情况下Button按钮的clickable = true,其他分析过程跟TextView是一样的,大家可以自己试着分析一下哈!通过这两篇的学习大家多Android事件处理机制是不是有了比较深刻的了解了呢?

点击下载源码

0 0