网上不少用源码来分析touch事件机制的文章,可是因为View和ViewGroup事件分发和android系统事件分开有关系,因此看起来有点云里雾里的,下面本身写了一个例子来讲嘛touch分发的原理,和咱们工做中遇到此类问题应该怎么处理这类事件,首先必须知道的一点是ViewGroup是继承至ViewG的,这个大家能够去源码中看看,接下来咱们来讲明ViewGroup和View下的三个相关Touch分发的函数android
接下来咱们先看看源码中的官方说明:git
/** * Pass the touch screen motion event down to the target view, or this * view if it is the target. * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ public boolean dispatchTouchEvent(MotionEvent event) { }
上面写的很清楚,分发事件到对应的view,这是View源码中的类,实际ViewGroup中的更复杂,他多了一个功能就是还要往子View分发事件。github
/** * Implement this method to handle touch screen motion events. * <p> * If this method is used to detect click actions, it is recommended that * the actions be performed by implementing and calling * {@link #performClick()}. This will ensure consistent system behavior, * including: * <ul> * <li>obeying click sound preferences * <li>dispatching OnClickListener calls * <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when * accessibility features are enabled * </ul> * * @param event The motion event. * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { }
上面这个方法就是咱们常常用到的,具体对touch事件的处理。算法
/** * Implement this method to intercept all touch screen motion events. This * allows you to watch events as they are dispatched to your children, and * take ownership of the current gesture at any point. * * <p>Using this function takes some care, as it has a fairly complicated * interaction with {@link View#onTouchEvent(MotionEvent) * View.onTouchEvent(MotionEvent)}, and using it requires implementing * that method as well as this one in the correct way. Events will be * received in the following order: * * <ol> * <li> You will receive the down event here. * <li> The down event will be handled either by a child of this view * group, or given to your own onTouchEvent() method to handle; this means * you should implement onTouchEvent() to return true, so you will * continue to see the rest of the gesture (instead of looking for * a parent view to handle it). Also, by returning true from * onTouchEvent(), you will not receive any following * events in onInterceptTouchEvent() and all touch processing must * happen in onTouchEvent() like normal. * <li> For as long as you return false from this function, each following * event (up to and including the final up) will be delivered first here * and then to the target's onTouchEvent(). * <li> If you return true from here, you will not receive any * following events: the target view will receive the same event but * with the action {@link MotionEvent#ACTION_CANCEL}, and all further * events will be delivered to your onTouchEvent() method and no longer * appear here. * </ol> * * @param ev The motion event being dispatched down the hierarchy. * @return Return true to steal motion events from the children and have * them dispatched to this ViewGroup through onTouchEvent(). * The current target will receive an ACTION_CANCEL event, and no further * messages will be delivered here. */ public boolean onInterceptTouchEvent(MotionEvent ev) { return false; }
上面这个函数能够看看应该,这个函数是用来拦截touch事件的,默认返回的是false,若是返回true,当前的View的dispatchTouchEvent()和onTouchEvent()还会运行,可是子View的相关函数将再也不运行。app
下面我用一个例子来讲明这个问题,我创建了一个工程,自定义了三个MyLinearLayout,MyLinearLayout1,MyLinearLayout2类继承至LinearLayout,一样的代码以下,可是有三个:ide
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); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.d("MyLinearLayout", "onInterceptTouchEvent"); return super.onInterceptTouchEvent(ev); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.d("MyLinearLayout", "dispatchTouchEvent"); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d("MyLinearLayout", "onTouchEvent"); return super.onTouchEvent(event); } }
还写了一个MyTextView类,继承于TextView,代码以下:函数
public class MyTestView extends TextView { public MyTestView(Context context) { super(context); } public MyTestView(Context context, AttributeSet attrs) { super(context, attrs); } public MyTestView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d("MyTestView", "dispatchTouchEvent"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d("MyTestView", "onTouchEvent"); return super.onTouchEvent(event); } }
个人布局代码代码以下:布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="github.lorcanluo.testdispatch.MainActivity"> <github.lorcanluo.testdispatch.MyLinearLayout android:layout_width="match_parent" android:layout_height="500dp" android:background="#ff0000" android:padding="20dp"> <github.lorcanluo.testdispatch.MyLinearLayout1 android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00ff00" android:padding="30dp"> <github.lorcanluo.testdispatch.MyLinearLayout2 android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:background="#0000ff"> <github.lorcanluo.testdispatch.MyTestView android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:background="#ffffff" android:text="我就是小打杂" /> </github.lorcanluo.testdispatch.MyLinearLayout2> </github.lorcanluo.testdispatch.MyLinearLayout1> </github.lorcanluo.testdispatch.MyLinearLayout> </RelativeLayout>
布局出来的效果如图:测试
接下来咱们来使用不一样的操做,来输出日志,首先看一下什么都没改的日志输出以下:ui
@Override public boolean onTouchEvent(MotionEvent event) { Log.d("MyTestView", "onTouchEvent"); return true; }
那么输出以下:
咱们能够看到日志中,只有MyTextView的onTouchEvent()事件了,这表示事件已经被咱们消耗了,父类不用再处理onTouchEvent()事件了,若是这里你手动返回false的话,那么父类的onTouchEvent()事件仍是会响应的。
@Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d("MyTestView", "dispatchTouchEvent"); return true; }
dispatchTouchEvent()函数中返回true之后,表示事件已经被"消耗",那么全部相关的onTouchEvent()将再也不输出,因此咱们得出的输出结果以下:
若是父类的dispatchTouchEvent()返回true以后,本类和父view的onTouchEvent()事件再也不调用,子类的全部touch事件再也不调用,这和接下来的onInterceptTouchEvent()仍是有区别,须要细心分别。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.d("MyLinearLayout1", "onInterceptTouchEvent"); return true; }
事件被拦截之后,子view的相关touch事件将再也不调用,可是本类和父类事件仍是要调用的,这里和上面dispatchTouchEvent()仍是有差异,须要仔细区分,咱们的输出以下:
在平常工做中,咱们仍是有可能遇到touch事件冲突的问题的,那么有了上面的知识,咱们能够经过以上函数处理的组合来处理事件冲突。
可是还可能有更为复杂的状况,这就须要你们去动态的算法处理了。。。。
本文的例子放在了:https://github.com/lorcanluo/testDispatchTouchEvent