android ViewGoup事件分发机制dispatchTouchEvent
来源:互联网 发布:加人软件使用视频 编辑:程序博客网 时间:2024/06/06 10:05
做本章内容给大家分享一下ViewGroup的事件分发中dispatchTouchEvent事件,这个事件大家很熟悉,但平时重写最多的是onInterceptTouchEvent,onTouchEvent,说到点击事件大家都会明白有三个重要的方法,下面结合源码、例子、图来分析dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent三者之间的关系
先上例子:
例子中,有4个ViewGroup,每个ViewGroup都重写了三个重要的方法,而且也重写了当前Activity的dispatchTouchEvent和onTouchEvent
ViewGroup之间的关系如下图
import android.os.Bundle;import android.util.Log;import android.view.MotionEvent;import android.view.ViewGroup;import android.widget.RelativeLayout;public class MainActivity extends Activity { private final String TAG = "WXQ"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); RelativeLayout viewA = new RelativeLayout(this){ @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(TAG, "viewA dispatchTouchEvent action=" + ev.getAction()); boolean value = super.dispatchTouchEvent(ev); Log.d(TAG, "viewA dispatchTouchEvent value=" + value); return value; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i(TAG, "viewA onInterceptTouchEvent action=" + ev.getAction()); boolean value = super.onInterceptTouchEvent(ev); Log.d(TAG, "viewA onInterceptTouchEvent value=" + value); return value; } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG, "viewA onTouchEvent action=" + event.getAction()); boolean value = super.onTouchEvent(event); Log.d(TAG, "viewA onTouchEvent value=" + value); return value; } }; RelativeLayout viewB = new RelativeLayout(this) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(TAG, "viewB dispatchTouchEvent action=" + ev.getAction()); boolean value = super.dispatchTouchEvent(ev); Log.d(TAG, "viewB dispatchTouchEvent value=" + value); return value; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i(TAG, "viewB onInterceptTouchEvent action=" + ev.getAction()); boolean value = super.onInterceptTouchEvent(ev); Log.d(TAG, "viewB onInterceptTouchEvent value=" + value); return value; } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG, "viewB onTouchEvent action=" + event.getAction()); boolean value = super.onTouchEvent(event); Log.d(TAG, "viewB onTouchEvent value=" + value); return value; } }; RelativeLayout viewC = new RelativeLayout(this) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(TAG, "viewC dispatchTouchEvent action=" + ev.getAction()); boolean value = super.dispatchTouchEvent(ev); Log.d(TAG, "viewC dispatchTouchEvent value=" + value); return value; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i(TAG, "viewC onInterceptTouchEvent action=" + ev.getAction()); boolean value = super.onInterceptTouchEvent(ev); Log.d(TAG, "viewC onInterceptTouchEvent value=" + value); return value; } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG, "viewC onTouchEvent action=" + event.getAction()); boolean value = super.onTouchEvent(event); Log.d(TAG, "viewC onTouchEvent value=" + value); return value; } }; RelativeLayout viewD = new RelativeLayout(this) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(TAG, "viewD dispatchTouchEvent action=" + ev.getAction()); boolean value = super.dispatchTouchEvent(ev); Log.d(TAG, "viewD dispatchTouchEvent value=" + value); return value; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i(TAG, "viewD onInterceptTouchEvent action=" + ev.getAction()); boolean value = super.onInterceptTouchEvent(ev); Log.d(TAG, "viewD onInterceptTouchEvent value=" + value); return value; } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG, "viewD onTouchEvent action=" + event.getAction()); boolean value = super.onTouchEvent(event); Log.d(TAG, "viewD onTouchEvent value=" + value); return value; } }; viewC.addView(viewD, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); viewA.addView(viewC, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); viewA.addView(viewB, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); setContentView(viewA); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(TAG, "activity dispatchTouchEvent action=" + ev.getAction()); boolean value = super.dispatchTouchEvent(ev); Log.d(TAG, "activity dispatchTouchEvent value=" + value); return value; } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG, "activity onTouchEvent action=" + event.getAction()); boolean value = super.onTouchEvent(event); Log.d(TAG, "activity onTouchEvent value=" + value); return value; }}
情景1:首先所有的返回值都不做改动,我们运行一下看看效果
activity dispatchTouchEvent action=0viewA dispatchTouchEvent action=0viewA onInterceptTouchEvent action=0viewA onInterceptTouchEvent value=falseviewB dispatchTouchEvent action=0viewB onInterceptTouchEvent action=0viewB onInterceptTouchEvent value=falseviewB onTouchEvent action=0viewB onTouchEvent value=falseviewB dispatchTouchEvent value=falseviewC dispatchTouchEvent action=0viewC onInterceptTouchEvent action=0viewC onInterceptTouchEvent value=falseviewD dispatchTouchEvent action=0viewD onInterceptTouchEvent action=0viewD onInterceptTouchEvent value=falseviewD onTouchEvent action=0viewD onTouchEvent value=falseviewD dispatchTouchEvent value=falseviewC onTouchEvent action=0viewC onTouchEvent value=falseviewC dispatchTouchEvent value=falseviewA onTouchEvent action=0viewA onTouchEvent value=falseviewA dispatchTouchEvent value=falseactivity onTouchEvent action=0activity onTouchEvent value=falseactivity dispatchTouchEvent value=falseactivity dispatchTouchEvent action=1activity onTouchEvent action=1activity onTouchEvent value=falseactivity dispatchTouchEvent value=falsedown事件,首先明白一点就是touch任何事件都是最先到Activity,Activity接收顺序为dispatchTouchEvent然后是onTouchEvent,然后再向下分发给ViewGroup,ViewGroup最先接收到的是dispatchTouchEvent方法,然后经过onInterceptTouchEvent 再到onTouchEvent ,先不要问为什么,一会儿咱们一起看源码就知道了。因为ViewGroup默认都是不可点击,所以所有的ViewGroup方法都返回了false,最终down事件又回到了Activity,所以Activity的dispatchTouchEvent的接收值为false
紧接着up事件,事件也是最先由当前Activity的dispatchTouchEvent来分发,然后就没再向下分发,为什么呢,再看一种情景就知道了
情景2:接下来我们做一点儿改动,我们将ViewB调用setClickable(true),使ViewB变成一个可点击的VIEW,运行一下看看会发生什么
activity dispatchTouchEvent action=0
viewA dispatchTouchEvent action=0
viewA onInterceptTouchEvent action=0
viewA onInterceptTouchEvent value=false
viewB dispatchTouchEvent action=0
viewB onInterceptTouchEvent action=0
viewB onInterceptTouchEvent value=false
viewB onTouchEvent action=0
viewB onTouchEvent value=true
viewB dispatchTouchEvent value=true
viewA dispatchTouchEvent value=true
activity dispatchTouchEvent value=true
activity dispatchTouchEvent action=1
viewA dispatchTouchEvent action=1
viewA onInterceptTouchEvent action=1
viewA onInterceptTouchEvent value=false
viewB dispatchTouchEvent action=1
viewB onTouchEvent action=1
viewB onTouchEvent value=true
viewB dispatchTouchEvent value=true
viewA dispatchTouchEvent value=true
activity dispatchTouchEvent value=true
我们看到有了很大变化,,由于ViewB已经变成了可点击的View所以ViewB的onTouchEvent返回了true,同时我们也看到了Activity的dispatchTouchEvent也返回true,接着up事件,也是先经过Activity再往下传递给ViewA再传递给ViewB然后就没再向下传了,为什么呢,因为ViewB是可点击的View,既然ViewB收到了down事件,那么move、up事件也一定能收到才是合理的完整事件对吧。
那你可能会问两个问题1、事件为什么不再向ViewC和ViewD传递了呢,2、为什么ViewA能收到事件。原因很简单,抛开程序思维去想,ViewA收到事件后检索自己的子View,ViewB收到事件了,再往下传已经没意义了,ViewB是ViewA的子View,事件必定会先经过父结点
我们看一下源码粗体部分可知,当前ViewGroup的dispatchTouchEvent被调用后,会把事件交给了onInterceptTouchEvent来做拦截处理
public boolean dispatchTouchEvent(MotionEvent ev) { boolean handled = false; if (onFilterTouchEventForSecurity(ev)) {........... // Check for interception. final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; }......... }.......... return handled; }
对子VIEW遍历,根据子VIEW的dispatchTouchEvent的返回值是否决定下一个事件继续分发
public boolean dispatchTouchEvent(MotionEvent ev) {.................. if (newTouchTarget == null && childrenCount != 0) { final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. final ArrayList<View> preorderedList = buildOrderedChildList(); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex); if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { continue; } newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); if (preorderedList != null) { // childIndex points into presorted list, find original index for (int j = 0; j < childrenCount; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } } if (preorderedList != null) preorderedList.clear(); }............... }
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; // Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. final int oldAction = event.getAction(); if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } // Calculate the number of pointers to deliver. final int oldPointerIdBits = event.getPointerIdBits(); final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; // If for some reason we ended up in an inconsistent state where it looks like we // might produce a motion event with no pointers in it, then drop the event. if (newPointerIdBits == 0) { return false; } // If the number of pointers is the same and we don't need to perform any fancy // irreversible transformations, then we can reuse the motion event for this // dispatch as long as we are careful to revert any changes we make. // Otherwise we need to make a copy. final MotionEvent transformedEvent; if (newPointerIdBits == oldPointerIdBits) { if (child == null || child.hasIdentityMatrix()) { if (child == null) { handled = super.dispatchTouchEvent(event); } else { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; event.offsetLocation(offsetX, offsetY); handled = child.dispatchTouchEvent(event); event.offsetLocation(-offsetX, -offsetY); } return handled; } transformedEvent = MotionEvent.obtain(event); } else { transformedEvent = event.split(newPointerIdBits); } // Perform any necessary transformations and dispatch. if (child == null) { handled = super.dispatchTouchEvent(transformedEvent); } else { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; transformedEvent.offsetLocation(offsetX, offsetY); if (! child.hasIdentityMatrix()) { transformedEvent.transform(child.getInverseMatrix()); } handled = child.dispatchTouchEvent(transformedEvent); } // Done. transformedEvent.recycle(); return handled; }对ViewGroup的dispatchTouchEvent方法的总结:方法中的super.dispatchTouchEvent去分发当前事件到自己的子View,而当前方法的返回值是告诉父View或Activity下一个事件(move、up)还需不需要给当前ViewGroup,它的返回值是由其所有子View来决定,如果所有子View都不可点返回false,如果有任何一个子View接收事件则返回true,回过头看看情景1知道为什么up事件Activity不再向ViewGroup传递,因为ViewA的dispatchTouchEvent在down事件时返回了false,说明ViewA的所有子View都没有接收down事件,所以ViewA告诉Activity说up事件不需要再给我了
0 0
- android ViewGoup事件分发机制dispatchTouchEvent
- android dispatchTouchEvent()事件分发机制
- Android viewGoup.dispatchTouchEvent(ev)源码
- android事件分发机制——dispatchTouchEvent方法
- Android的事件分发机制(一):dispatchTouchEvent
- Android dispatchTouchEvent View事件分发
- Android事件分发之dispatchTouchEvent()
- android事件分发 入口(dispatchTouchEvent)
- Android 关于事件分发 dispatchTouchEvent
- Android dispatchTouchEvent touch事件的分发
- android 事件分发 拦截 (onInterceptTouchEvent dispatchTouchEvent onTouchEvent)
- android 事件分发之dispatchTouchEvent()用法
- Android 事件分发(dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent)
- Android进阶——Android事件分发机制之dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
- Android事件分发机制完全解析,带你从源码的角度彻底理解dispatchTouchEvent,onInterceptTouchEvent
- Android中Touch事件分发机制-关于dispatchTouchEvent等方法的研究
- Android 3步搞定事件分发机制,再也不用担心onTouch和onTouchEvent&dispatchTouchEvent
- dispatchTouchEvent分发机制
- @RequestBody, @ResponseBody 注解详解
- java学习笔记11——集合类详解
- Result-Global_Results_全局结果集
- Debian系统备份成iso镜像的工具安装启动
- POJ1837:Balance
- android ViewGoup事件分发机制dispatchTouchEvent
- xtrabackup 全备、增量备份恢复
- SpringMVCHelloWorld
- 求解一个矩阵的逆矩阵
- 单例模式
- The following classes could not be instantiated: android.support.v7.widget.RecyclerView v7中找不到的终极方法
- javascript 闭包的理解以及它的好处与坏处
- Spark 随机森林算法原理、源码分析及案例实战
- 关于控件滑动至顶部的监听