Android ViewGroup事件分发机制

转载请标明出处:,本文出自【张鸿洋的博客】

上一篇已经完整的解析了Android View的事件分发机制,今天给大家代码ViewGroup事件分发的源码解析~~凡是自定义ViewGroup实现各种滑动效果的,不可避免的会出现很多事件的冲突,对ViewGroup事件分发机制的了解,也有益于大家了解冲突产生的原因,以及对冲突进行处理~

1、案例

首先我们接着上一篇的代码,在代码中添加一个自定义的LinearLayout:

package com.example.zhy_event03;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.LinearLayout;public class MyLinearLayout extends LinearLayout{private static final String TAG = MyLinearLayout.class.getSimpleName();public MyLinearLayout(Context context, AttributeSet attrs){super(context, attrs);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev){int action = ev.getAction();switch (action){case MotionEvent.ACTION_DOWN:Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.e(TAG, "dispatchTouchEvent ACTION_UP");break;default:break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event){int action = event.getAction();switch (action){case MotionEvent.ACTION_DOWN:Log.e(TAG, "onTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.e(TAG, "onTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.e(TAG, "onTouchEvent ACTION_UP");break;default:break;}return super.onTouchEvent(event);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev){int action = ev.getAction();switch (action){case MotionEvent.ACTION_DOWN:Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.e(TAG, "onInterceptTouchEvent ACTION_UP");break;default:break;}return super.onInterceptTouchEvent(ev);}@Overridepublic void requestDisallowInterceptTouchEvent(boolean disallowIntercept){Log.e(TAG, "requestDisallowInterceptTouchEvent ");super.requestDisallowInterceptTouchEvent(disallowIntercept);}}继承LinearLayout,然后复写了与事件分发机制有关的代码,添加上了日志的打印~

然后看我们的布局文件:

<com.example.zhy_event03.MyLinearLayout xmlns:android=""xmlns:tools=""android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity" ><com.example.zhy_event03.MyButtonandroid:id="@+id/id_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="click me" /></com.example.zhy_event03.MyLinearLayout>MyLinearLayout中包含一个MyButton,MyButton都上篇博客中已经出现过,这里就不再贴代码了,不清楚可以去查看~

然后MainActivity就是直接加载布局,没有任何代码~~~

直接运行我们的代码,然后点击我们的Button,依然是有意的MOVE一下,不然不会触发MOVE事件,看一下日志的输出:

09-06 09:57:27.287: E/MyLinearLayout(959): dispatchTouchEvent ACTION_DOWN09-06 09:57:27.287: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_DOWN09-06 09:57:27.287: E/MyButton(959): dispatchTouchEvent ACTION_DOWN09-06 09:57:27.297: E/MyButton(959): onTouchEvent ACTION_DOWN09-06 09:57:27.297: E/MyButton(959): onTouchEvent ACTION_MOVE09-06 09:57:27.327: E/MyLinearLayout(959): dispatchTouchEvent ACTION_MOVE09-06 09:57:27.327: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_MOVE09-06 09:57:27.337: E/MyButton(959): dispatchTouchEvent ACTION_MOVE09-06 09:57:27.337: E/MyButton(959): onTouchEvent ACTION_MOVE09-06 09:57:27.457: E/MyLinearLayout(959): dispatchTouchEvent ACTION_UP09-06 09:57:27.457: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_UP09-06 09:57:27.457: E/MyButton(959): dispatchTouchEvent ACTION_UP09-06 09:57:27.457: E/MyButton(959): onTouchEvent ACTION_UP可以看到大体的事件流程为:

MyLinearLayout的dispatchTouchEvent ->MyLinearLayout的onInterceptTouchEvent ->MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent

可以看出,在View上触发事件,最先捕获到事件的为View所在的ViewGroup,然后才会到View自身~

下面我们按照日志的输出,进入源码~

2、源码分析

ViewGroup -dispatchTouchEvent

1、ViewGroup -dispatchTouchEvent – ACTION_DOWN

首先是ViewGroup的dispatchTouchEvent方法:

@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {if (!onFilterTouchEventForSecurity(ev)) {return false;}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) {// this is weird, we got a pen down, but we thought it was// already down!// XXX: We should probably send an ACTION_UP to the current// target.mMotionTarget = null;}// If we’re disallowing intercept or if we’re allowing and we didn’t// interceptif (disallowIntercept || !onInterceptTouchEvent(ev)) {// reset this event’s action (just to protect ourselves)ev.setAction(MotionEvent.ACTION_DOWN);// We know we want to dispatch the event down, find a child// who can handle it, start with the front-most child.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)) {// offset the event to the view’s coordinate systemfinal 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)) {// Event handled, we have a target now.mMotionTarget = child;return true;}// The event didn’t get handled, try the next view.// Don’t reset the event’s location, it’s not// necessary here.}}}}}….//other code omitted代码比较长,决定分段贴出,首先贴出的是ACTION_DOWN事件相关的代码。

16行:进入ACTION_DOWN的处理

17-23行:将mMotionTarget置为null

26行:进行判断:if(disallowIntercept || !onInterceptTouchEvent(ev))

两种可能会进入IF代码段

1、当前不允许拦截,即disallowIntercept =true,

2、当前允许拦截但是不拦截,即disallowIntercept =false,但是onInterceptTouchEvent(ev)返回false ;

注:disallowIntercept 可以通过viewGroup.requestDisallowInterceptTouchEvent(boolean);进行设置,后面会详细说;而onInterceptTouchEvent(ev)可以进行复写。

36-57行:开始遍历所有的子View

从此便踏上征途,也许会孤独一程。

Android ViewGroup事件分发机制

相关文章:

你感兴趣的文章:

标签云: