一个实例让你透彻理解Android ViewGroupe的事件分发
2017-01-23 15:57
447 查看
前言:ViewGroupe的事件分发是一个重点也是一个痛点,许多地方都会用到,尤其是滑动冲突时的解决,下面通过一个实例并结合源码来分析ViewGroupe的事件分发。前提是有一定的View事件分发基础,不熟悉的可以看我之前写的View的事件分发。
自定义的Button:
自定义的TextView:
自定义的LinearLayout:
上面的自定义view都很简单,增加了日志打印而已。
MainActivity如下:
当我们点击button的时候,log如下:
点击TextView的时候,log如下:
点击空白地方的log如下:
为什么会出现如上的结果呢,我们通过ViewGroupe的源码来分析,ViewGroupe的dispatchTouchEvent源码如下:
View事件分发是从Activity的DecorView一直往下传递,直到我们的Linearlayout再到我们手指触摸的view,比如我们代码中的Button、TextView以及Linearlayout的空白部分。
知道上面的原则,我们接着往下分析点击Button的Log,首先从Activity传递到我们的布局LinearLayout中,由于LinearLayout的父布局没有设置事件拦截,所以走到代码的61行:
61行的child就表示我们点击的button,button是View所以走View的dispatchTouchEvent,
由于button是可点击的所以消费了ACTION_DOWN事件,所以61行返回true,往下走,到67行:
mMotionTarget = child; //这里的child就是Button
然后在71行返回true,ACTION_DOWN事件执行完毕。
然后是 ACTION_MOVE:
97行:final View target = mMotionTarget;
102行的 target 不为空,120行没有拦截,直接走到165行–target.dispatchTouchEvent(ev); 即走到MyButton的onTouchEvent
ACTION_UP : 和ACTION_MOVE没有拦截的话基本一样就不分析了。
下面分析点击MyTextView的log,
走到61行 ,此时的 child 就是我们点击的 MyTextView了,由于 MyTextView是继承自 TextView ,所以 MyTextView默认也是不可点击的,所以, 61行的child.dispatchTouchEvent(ev)返回的是false,所以我们需要往上看往上看MyLinearLayout的dispatchTouchEvent()方法的61行, 由于由于TextView.dispatchTouchEvent()为false, 导致mMotionTarget没有被赋值,
那么就得继续往下走,走到97行:
final View target = mMotionTarget; //此时由于MyTextView没有消费down事件,所以mMotionTarget = null,所以导致 target 为 null,
所以会走到110行:return super.dispatchTouchEvent(ev); 这里调用的是MyLinearLayout父类View的dispatchTouchEvent()方法,由于MyLinearLayout没有设置onTouchListener,但是重写了onTouchEvent方法,但是由于MyLinearLayout既不是clickable的也是longClickable的,所以其onTouchEvent()方法false,所以dispatchTouchEvent()也是返回false。这也表示 ACTION_DOWN未被消费,从而不能继续执行下面的ACTION_MOVE, ACTION_UP等事件了,具体为什么,代码中没看出来,如果知道原因的小伙伴可以告诉我啊。
点击MyLinearLayout的空白区域和点击MyTextView情况类型,就不作过多分析啦,有问题就留言大家一起讨论。
现在我们看看事件的拦截,我们修改下MyLinearLayout中的onInterceptTouchEvent的方法,现将 ACTION_MOVE中return true,表示拦截move,以及move的后续事件(up等)
然后我们点击MyButton,我们先分析结果,等下再看看log,由于是点击button而且是在down事件之后的move中拦截事件的,所以MyButton的down事件会完整的走完,我们接着看move事件。
move :走到97行,对 target 进行赋值: final View target = mMotionTarget; mMotionTarget 就是在down事件中进行赋值的 MyButton。
走到120行:if (!disallowIntercept && onInterceptTouchEvent(ev)) ,disallowIntercept 默认是false的,由于在onInterceptTouchEvent的move中 返回true,所以 onInterceptTouchEvent(ev) = true,这个if语句成立,
走到127 行 :ev.setAction(MotionEvent.ACTION_CANCEL); 将action设为 ACTION_CANCEL。
132行:if (!target.dispatchTouchEvent(ev)),就调用MyButton的dispatchTouchEvent方法,由于127行,将将action设为 了ACTION_CANCEL,所以会调用 MyButton的dispatchTouchEvent方法中的ACTION_CANCEL,日志中会提现出来。
140行:mMotionTarget = null; 将 mMotionTarget 置为null。
144行:return true; 表示move事件一被消费掉。
接着看UP事件:
同样是走到97行为target赋值,final View target = mMotionTarget; 但此时的mMotionTarget在move事件中已被置为null,所以target也是null;
102行:if (target == null) ,成立,走到该条件语句的110行:return super.dispatchTouchEvent(ev);
即调用 MyLinearLayout的父类的dispatchTouchEvent方法,即View的dispatchTouchEvent,MyLinearLayout中没有设置onTouchListener,但是重写了 onTouchEvent方法,由于MyLinearLayout不是clickable以及longclickable所以 onTouchEvent 返回false,所以 dispatchTouchEvent也返回false,UP事件未被消费。
现在看看log,log如下:
log和我们的分析是一致的。
现在再看看我们在MyLinearLayout的onInterceptTouchEvent方法的down事件中,进行事件拦截,代码如下:
我们还是先分析源码再提高log做验证。
DOWN事件:
由于在down事件进行了拦截,所以走到31行:if (disallowIntercept || !onInterceptTouchEvent(ev))中的onInterceptTouchEvent为true,所以该条件不成立。
102行:if (target == null)由于down事件未被消费,所以 target 为null,走到 110行:return super.dispatchTouchEvent(ev); 跟之前的分析一样,由于MyLinearLayout重写了onTouchEvent方法,由于MyLinearLayout不是clickable以及longclickable,所以 onTouchEvent 返回false,所以 dispatchTouchEvent也返回false,Down事件未被消费。
由于Down事件未被消费,所以接下来的MOVE和UP事件不执行了,这个问题上面有提到,我也不知原因。
log如下:
如果MyLinearLayout的onInterceptTouchEvent(ev) 当ACTION_MOVE时return true ,即拦截子View的MOVE以及UP事件;
此时子View希望依然能够响应MOVE和UP时我们应该怎么办呢?
我们可以如下做,比如我们的MyButton中的代码:
结果代码就可以正常执行下去了,这个源码分析不难,就不做讲解了,log如下:
好了,文章就到这里了,有问题请留言,一起学习,一起进步。
自定义的Button:
package com.zjt.touch; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.Button; public class MyButton extends Button { private final static String TAG = "MyButton"; public MyButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MyButton(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { //解决父类拦截事件的调用方法 //getParent().requestDisallowInterceptTouchEvent(true); switch (event.getAction()) { 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; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL"); break; default: break; } return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "onTouchEvent ACTION_DOWN"); // return false; 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); } }
自定义的TextView:
package com.zjt.touch; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.TextView; import com.zjt.view.TestView; /** * Created by hc on 2017/1/23. */ public class MyTextView extends TextView { private final static String TAG = "MyTextView"; public MyTextView(Context context) { super(context); } public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); } public MyTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP"); break; case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "onTouchEvent ACTION_UP"); break; case MotionEvent.ACTION_DOWN: Log.e(TAG, "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onTouchEvent ACTION_MOVE"); break; } return super.onTouchEvent(event); } }
自定义的LinearLayout:
package com.zjt.touch; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; /** * Created by hc on 2017/1/23. */ public class MyLinearLayout extends LinearLayout { private final static String TAG = "MyLinearLayout"; public MyLinearLayout(Context context) { super(context); } public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP"); break; // return true; case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "onInterceptTouchEvent ACTION_UP"); break; //return false; case MotionEvent.ACTION_DOWN: Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN"); // return false; break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE"); // return true; break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "onTouchEvent ACTION_UP"); break; case MotionEvent.ACTION_DOWN: Log.e(TAG, "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onTouchEvent ACTION_MOVE"); break; } return super.onTouchEvent(event); } }
上面的自定义view都很简单,增加了日志打印而已。
MainActivity如下:
package com.zjt.touch; import com.example.testactivity2.R; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.View.OnTouchListener; import android.widget.Button; public class TestViewTouchActivity extends Activity implements OnClickListener{ private final static String TAG = "TestViewTouchActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.view_touch_layout); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_touch: Log.e(TAG, "btn click......"); break; default: break; } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP"); break; case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "onTouchEvent ACTION_UP"); break; case MotionEvent.ACTION_DOWN: Log.e(TAG, "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onTouchEvent ACTION_MOVE"); break; } return super.onTouchEvent(event); } }
当我们点击button的时候,log如下:
点击TextView的时候,log如下:
点击空白地方的log如下:
为什么会出现如上的结果呢,我们通过ViewGroupe的源码来分析,ViewGroupe的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; /** * disallowIntercept表示是否允许事件拦截,默认是false,即不拦截事件 * 此值可以通过requestDisallowInterceptTouchEvent(boolean disallowIntercept)方法进行设置 */ boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (action == MotionEvent.ACTION_DOWN) { /** * 因为从ACTION_DOWN开始要开启新一轮的事件分发,所有要将mMotionTarget(目标)置为空 */ 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 // intercept /** * 当disallowIntercept(默认是false)为true,或者onInterceptTouchEvent(ev)(默认返回为false)方法的返回值 * 为false,取反则为true,则判断条件成立 */ if (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; /** * 遍历当前ViewGroup的所有子View */ for (int i = count - 1; i >= 0; i--) { final View child = children[i]; /** * 如果当前的View是VISIBLE的或者有动画执行 */ if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { child.getHitRect(frame); /** * 如果子View包含当前触摸的点 */ if (frame.contains(scrolledXInt, scrolledYInt)) { // offset the event to the view's coordinate system 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)) { // Event handled, we have a target now. /** * 如果子View的dispatchTouchEvent方法的返回值为true,则表示子View已经消费了事件 * 此时将子View赋值给mMotionTarget */ mMotionTarget = child; /** * 直接返回true,表示down事件被消费掉了 */ return true; } // The event didn't get handled, try the next view. // Don't reset the event's location, it's not // necessary here. } } } } } boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || (action == MotionEvent.ACTION_CANCEL); if (isUpOrCancel) { // Note, we've already copied the previous state to our local // variable, so this takes effect on the next event /** * 如果是ACTION_UP或者ACTION_CANCEL, 将disallowIntercept设置为默认的false * 因为ACTION_UP或者ACTION_CANCEL表示事件执行完,要将前面设置的值复位 */ mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } // The event wasn't an ACTION_DOWN, dispatch it to our target if // we have one. final View target = mMotionTarget; /** * 当mMotionTarget为空表示没有找到消费事件的View,此时需要调用ViewGroup父类的dispatchTouchEvent方法, * ViewGroup的父类即为View */ if (target == null) { // We don't have a target, this means we're handling the // event as a regular view. 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 have a target, see if we're allowed to and want to intercept its // events /** * 如果执行到此说明target!=null,然后判断是否允许拦截和是否想要拦截 * 如果允许拦截(!disallowIntercept=true),并且想要拦截(onInterceptTouchEvent(ev)返回值为true) * 则条件成立 */ if (!disallowIntercept && onInterceptTouchEvent(ev)) { final float xc = scrolledXFloat - (float) target.mLeft; final float yc = scrolledYFloat - (float) target.mTop; mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; /** * 设置动作为ACTION_CANCEL,此处与ev.getAction相对应 */ ev.setAction(MotionEvent.ACTION_CANCEL); /** * */ ev.setLocation(xc, yc); if (!target.dispatchTouchEvent(ev)) { // target didn't handle ACTION_CANCEL. not much we can do // but they should have. } // clear the target /** * */ mMotionTarget = null; // Don't dispatch this event to our own view, because we already // saw it when intercepting; we just want to give the following // event to the normal onTouchEvent(). return true; } if (isUpOrCancel) { mMotionTarget = null; } // finally offset the event to the target's coordinate system and // dispatch the event. 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; } /** * 将事件分发给target返回target.dispatchTouchEvent(ev)的返回值 */ return target.dispatchTouchEvent(ev); }
View事件分发是从Activity的DecorView一直往下传递,直到我们的Linearlayout再到我们手指触摸的view,比如我们代码中的Button、TextView以及Linearlayout的空白部分。
知道上面的原则,我们接着往下分析点击Button的Log,首先从Activity传递到我们的布局LinearLayout中,由于LinearLayout的父布局没有设置事件拦截,所以走到代码的61行:
61行的child就表示我们点击的button,button是View所以走View的dispatchTouchEvent,
由于button是可点击的所以消费了ACTION_DOWN事件,所以61行返回true,往下走,到67行:
mMotionTarget = child; //这里的child就是Button
然后在71行返回true,ACTION_DOWN事件执行完毕。
然后是 ACTION_MOVE:
97行:final View target = mMotionTarget;
102行的 target 不为空,120行没有拦截,直接走到165行–target.dispatchTouchEvent(ev); 即走到MyButton的onTouchEvent
ACTION_UP : 和ACTION_MOVE没有拦截的话基本一样就不分析了。
下面分析点击MyTextView的log,
走到61行 ,此时的 child 就是我们点击的 MyTextView了,由于 MyTextView是继承自 TextView ,所以 MyTextView默认也是不可点击的,所以, 61行的child.dispatchTouchEvent(ev)返回的是false,所以我们需要往上看往上看MyLinearLayout的dispatchTouchEvent()方法的61行, 由于由于TextView.dispatchTouchEvent()为false, 导致mMotionTarget没有被赋值,
那么就得继续往下走,走到97行:
final View target = mMotionTarget; //此时由于MyTextView没有消费down事件,所以mMotionTarget = null,所以导致 target 为 null,
所以会走到110行:return super.dispatchTouchEvent(ev); 这里调用的是MyLinearLayout父类View的dispatchTouchEvent()方法,由于MyLinearLayout没有设置onTouchListener,但是重写了onTouchEvent方法,但是由于MyLinearLayout既不是clickable的也是longClickable的,所以其onTouchEvent()方法false,所以dispatchTouchEvent()也是返回false。这也表示 ACTION_DOWN未被消费,从而不能继续执行下面的ACTION_MOVE, ACTION_UP等事件了,具体为什么,代码中没看出来,如果知道原因的小伙伴可以告诉我啊。
点击MyLinearLayout的空白区域和点击MyTextView情况类型,就不作过多分析啦,有问题就留言大家一起讨论。
现在我们看看事件的拦截,我们修改下MyLinearLayout中的onInterceptTouchEvent的方法,现将 ACTION_MOVE中return true,表示拦截move,以及move的后续事件(up等)
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "onInterceptTouchEvent ACTION_UP"); break; // return false; case MotionEvent.ACTION_DOWN: Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN"); // return false; break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE"); return true; // break; } return super.onInterceptTouchEvent(ev); }
然后我们点击MyButton,我们先分析结果,等下再看看log,由于是点击button而且是在down事件之后的move中拦截事件的,所以MyButton的down事件会完整的走完,我们接着看move事件。
move :走到97行,对 target 进行赋值: final View target = mMotionTarget; mMotionTarget 就是在down事件中进行赋值的 MyButton。
走到120行:if (!disallowIntercept && onInterceptTouchEvent(ev)) ,disallowIntercept 默认是false的,由于在onInterceptTouchEvent的move中 返回true,所以 onInterceptTouchEvent(ev) = true,这个if语句成立,
走到127 行 :ev.setAction(MotionEvent.ACTION_CANCEL); 将action设为 ACTION_CANCEL。
132行:if (!target.dispatchTouchEvent(ev)),就调用MyButton的dispatchTouchEvent方法,由于127行,将将action设为 了ACTION_CANCEL,所以会调用 MyButton的dispatchTouchEvent方法中的ACTION_CANCEL,日志中会提现出来。
140行:mMotionTarget = null; 将 mMotionTarget 置为null。
144行:return true; 表示move事件一被消费掉。
接着看UP事件:
同样是走到97行为target赋值,final View target = mMotionTarget; 但此时的mMotionTarget在move事件中已被置为null,所以target也是null;
102行:if (target == null) ,成立,走到该条件语句的110行:return super.dispatchTouchEvent(ev);
即调用 MyLinearLayout的父类的dispatchTouchEvent方法,即View的dispatchTouchEvent,MyLinearLayout中没有设置onTouchListener,但是重写了 onTouchEvent方法,由于MyLinearLayout不是clickable以及longclickable所以 onTouchEvent 返回false,所以 dispatchTouchEvent也返回false,UP事件未被消费。
现在看看log,log如下:
log和我们的分析是一致的。
现在再看看我们在MyLinearLayout的onInterceptTouchEvent方法的down事件中,进行事件拦截,代码如下:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "onInterceptTouchEvent ACTION_UP"); break; // return false; case MotionEvent.ACTION_DOWN: Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN"); return true; // break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE"); // return true; break; } return super.onInterceptTouchEvent(ev); }
我们还是先分析源码再提高log做验证。
DOWN事件:
由于在down事件进行了拦截,所以走到31行:if (disallowIntercept || !onInterceptTouchEvent(ev))中的onInterceptTouchEvent为true,所以该条件不成立。
102行:if (target == null)由于down事件未被消费,所以 target 为null,走到 110行:return super.dispatchTouchEvent(ev); 跟之前的分析一样,由于MyLinearLayout重写了onTouchEvent方法,由于MyLinearLayout不是clickable以及longclickable,所以 onTouchEvent 返回false,所以 dispatchTouchEvent也返回false,Down事件未被消费。
由于Down事件未被消费,所以接下来的MOVE和UP事件不执行了,这个问题上面有提到,我也不知原因。
log如下:
如果MyLinearLayout的onInterceptTouchEvent(ev) 当ACTION_MOVE时return true ,即拦截子View的MOVE以及UP事件;
此时子View希望依然能够响应MOVE和UP时我们应该怎么办呢?
我们可以如下做,比如我们的MyButton中的代码:
@Override public boolean dispatchTouchEvent(MotionEvent event) { //解决父类拦截事件的调用方法 getParent().requestDisallowInterceptTouchEvent(true); switch (event.getAction()) { 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; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL"); break; default: break; } return super.dispatchTouchEvent(event); }
修改MyLinearLayout的onInterceptTouchEvent(ev)的方法,在move中拦截事件,
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_UP: Log.e(TAG, "onInterceptTouchEvent ACTION_UP"); break; // return false; case MotionEvent.ACTION_DOWN: Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN"); // return true; break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE"); return true; // break; } return super.onInterceptTouchEvent(ev); }
结果代码就可以正常执行下去了,这个源码分析不难,就不做讲解了,log如下:
好了,文章就到这里了,有问题请留言,一起学习,一起进步。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件