提到事件处理,当对一个Button同时设置了setOnTouchListener和setOnClickListener后,如果OnTouchListener的onTouch方法return 了false,那么两个方法都会执行。如果return了true,则只有onTouch被执行,onClick不会被执行。
现在通过源码分析原因:
View.java#dispatchTouchEvent()
...
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//mListenerInfo是单例处理返回的对象
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
这个mListenerInfo就是单例处理返回的一个ListenerInfo 对象,这就保证了mListenerInfo 肯定不为null,即li != null:
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
它里面有着众多xxxListener,如:
public OnClickListener mOnClickListener;
protected OnLongClickListener mOnLongClickListener;
private OnTouchListener mOnTouchListener;
而我们setOnTouchListener时,就会将new出来的OnTouchListener 赋值过来,这就保证了li.mOnTouchListener != null。
我们setOnTouchListener重写的onTouch方法的返回值起到了决定性的作用,它影响了if条件语句的执行。如果return true,那就所有的条件都满足,万事大吉,程序走进if里,result = true。如果重写的onTouch方法return了false,那么整个if条件语句就不满足,就不再会往里走了。
在仔细看if块:
if (!result && onTouchEvent(event)) {
result = true;
}
如果result为true的话,那么“&&”前面就是false,onTouchEvent(event)就没有再执行的必要了。onTouchEvent负责事件的消费,而onClick是在MotionEvent.ACTION_UP中被调用—>performClickInternal()—>performClick():
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//这才是亮点!!!
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
这个if判断和刚才onTouch时的if是十分类似的。前面已经分析过了,if里面的条件都满足,于是就往里走,调用了 li.mOnClickListener.onClick(this)。至此,我们setOnClickListener就会响应点击事件了。
总结一下,TouchEvent发射出来一个信号,如果被onTouch拦截到了,自然就没onClick什么事了。可如果一旦onTouch没拦截到这个信号,onClick就会成功处理这个信号。
事件产生后的传递顺序如下:
来看ViewGroup对事件的分发过程,dispatchTouchEvent方法:
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 判断为DOWN事件,清除掉一些变量,重置状态
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// 没有拦截,为false
final boolean intercepted;
//当事件被ViewGroup的子元素成功处理时,mFirstTouchTarget会被赋值并指向子元素
//换句话说,当ViewGroup不拦截,并将事件交给子元素处理时,mFirstTouchTarget != null成立
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;
}
首先判断为ACTION_DOWN事件,会清除掉一些变量、重置状态。
后续有一个变量:final boolean intercepted。如果没有拦截,intercepted为false。这里有一个特殊情况:标志位FLAG_DISALLOW_INTERCEPT。这个标志位是在requestDisallowInterceptTouchEvent方法内设置的,一旦它被设置,那么ViewGroup就无法拦截除DOWN事件以外的其他事件(MOVE/UP等事件)。当ViewGroup分发事件时,DOWN事件会重置该标志位,导致子View的“努力徒劳”。当ViewGroup面临DOWM事件时,总会调用自己的onInterceptTouchEvent方法询问是否需要拦截事件。resetTouchState方法会对该标志位进行重置,故子View调用requestDisallowInterceptTouchEvent方法并不能影响ViewGroup对DOWM事件的处理结果,这一点,在使用内部拦截法处理滑动冲突时会得到验证。
然后往下走,
if (newTouchTarget == null && childrenCount != 0)
newTouchTarget 肯定为null,因为上面没几行刚刚赋值为null。childrenCount 表示控件的个数,肯定不为0,所以就会继续往里走下去,会遇到
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
buildTouchDispatchChildList()—>return buildOrderedChildList():对子view进行排序。
ArrayList<View> buildOrderedChildList() {
final int childrenCount = mChildrenCount;
if (childrenCount <= 1 || !hasChildWithZ()) return null;
if (mPreSortedChildren == null) {
//专门用来存放排序后的child的
mPreSortedChildren = new ArrayList<>(childrenCount);
} else {
// callers should clear, so clear shouldn't be necessary, but for safety...
mPreSortedChildren.clear();
mPreSortedChildren.ensureCapacity(childrenCount);
}
final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i++) {
// add next child (in child order) to end of list
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View nextChild = mChildren[childIndex];
//获取View在Z轴方向的值
final float currentZ = nextChild.getZ();
// insert ahead of any Views with greater Z
int insertIndex = i;
//mPreSortedChildren 就是上面刚刚new出来的数组。专门用来存放排序后的children
//从数组取出来和当前的currentZ比较,while里就是将Z值较大的往后放,较小的往前排。
while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
insertIndex--;
}
mPreSortedChildren.add(insertIndex, nextChild);
}
return mPreSortedChildren;
}
for循环中通过nextChild.getZ()来获取到currentZ,currentZ就是View在Z轴方向上的值(码积木一样,View都是一层层码上去的)。
越靠上的View,它的currentZ就越大。mPreSortedChildren 用来存放排序后的children。while里将Z值大的View放后,Z值小的往前排。
最后将排序好的数组返回。
preorderedList 就是return的排好序的数组,随后还会经历一个for循环。对排好序的数组的循环从childrenCount -1开始,也就是先从数组最后开始。通过getAndVerifyPreorderedIndex方法获取对应的childIndex,并进一步获取到响应的child(就是我们的Button等控件View)。
//从数组最后面,currentZ最大的开始
for (int i = childrenCount - 1; i >= 0; i--) {
//先拿到View的下标
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
//根据下标,得到View(如:Button)
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
//拿到Button后,canViewReceivePointerEvents判断点击是否是动画、是否可见,
//isTransformedTouchPointInView判断点击是否在View上。
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
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);
//dispatchTransformedTouchEvent负责将事件分发给谁来处理事件,return handled = child.dispatchTouchEvent(transformedEvent);
//这意味这事件已经传递到View中了。
//View#dispatchTouchEvent的返回结果--->result 只有当onTouchEvent处理后,result才为true。
//换句话说,当Button处理了事件,才会命中这个if,break掉for循环;如果Button没处理,就开始下一次循环,取出比Button的currentZ值更小的父容器,将事件交给它。
//Button处理事件就处理,Button不处理,就往上传递交给Button的父容器。如果父容器也不处理,那就交给父容器的父容器......
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();
//Button处理事件后,调用addTouchTarget方法--->mFirstTouchTarget!=null--->newTouchTarget==mFirstTouchTarget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
将事件从ViewGroup分发给View,这样事件的处理就走到了View上,于是事件就得到了它应有的处理(这个方法不止一次被调用,而且有很大区别,第三个参数是否为null):
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
......
//child不为null,就执行else中的child.dispatchTouchEvent(transformedEvent)方法
//也就是调用Button的dispatchTouchEvent方法。
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());
}
//这调用的就是Button的dispatchTouchEvent方法,即事件已经传递到View中了
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
返回的handled,即handled = child.dispatchTouchEvent(transformedEvent)
也就是返回View#dispatchTouchEvent的返回结果--->result
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//当onTouchEvent处理后,result才为true。
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
再回到ViewGroup#dispatchTouchEvent中的if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign))中。如果事件被Button处理完,会返回handled (= child.dispatchTouchEvent(transformedEvent)),View#dispatchTouchEvent方法的返回值和View自己的onTouchEvent有关。看ViewGroup#dispatchTouchEvent,如果Button没有处理事件,就会开启下一个循环,currentZ会更小,事件就会流转到父容器的手里。如果父容器也不处理,那就继续向上return。
可是如果在for循环中,Button处理掉了事件,就会break,即终止了for循环,也就是终止了事件的分发,别人就处理不了了。所以如果Button一旦处理了,Button的父容器就拿不到这个事件了。
当Button处理完事件后,调用addTouchTarget方法:
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
意味着newTouchTarget==mFirstTouchTarget、alreadyDispatchedToNewTouchTarget = true
再往后,mFirstTouchTarget != null,走else:
// Button处理事件后,mFirstTouchTarget != null,进入else
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//这个while循环内的操作,意味着while只会进去一次。
while (target != null) {
//next为null
final TouchTarget next = target.next;
//alreadyDispatchedToNewTouchTarget就是true,上面刚刚给赋的值
//两个条件都满足,即handled=true;while只循环一次,故退出循环后,直接就将handled = true返回了。
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
//将null给到target
target = next;
}
}
最后将handled返回就完事了。
说起滑动冲突,我们都遇到过。ViewPager嵌套ListView,如果ViewPager的onInterceptTouchEvent return了true后,ListView的世界都坍塌了。
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;
}
是否执行onInterceptTouchEvent,取决于disallowIntercept是否为false。假设onInterceptTouchEvent被执行了,即intercepted=true,下面的if 就不会再走了:
//没拦截,就往里进了
if (!canceled && !intercepted) {
// If the event is targeting accessibility focus we give it to the
// view that has accessibility focus and if it does not handle it
// we clear the flag and dispatch the event to all children as usual.
// We are looking up the accessibility focused host to avoid keeping
// state since these events are very rare.
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
//newTouchTarget在上面没几行就置空了。
//childrenCount 表示控件的个数,肯定不为0
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// buildTouchDispatchChildList:对子View根据currentZ进行排序
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//从数组最后面,currentZ最大的开始
for (int i = childrenCount - 1; i >= 0; i--) {
//先拿到View的下标
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
//根据下标,得到View(如:Button)
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
//拿到Button后,canViewReceivePointerEvents判断点击是否是动画、是否可见,
//isTransformedTouchPointInView判断点击是否在View上。
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
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);
//dispatchTransformedTouchEvent负责将事件分发给谁来处理事件,return handled = child.dispatchTouchEvent(transformedEvent);
//这意味这事件已经传递到View中了。
//View#dispatchTouchEvent的返回结果--->result 只有当onTouchEvent处理后,result才为true。
//换句话说,当Button处理了事件,才会命中这个if,break掉for循环;如果Button没处理,就开始下一次循环,取出比Button的currentZ值更小的父容器,将事件交给它。
//Button处理事件就处理,Button不处理,就往上传递交给Button的父容器。如果父容器也不处理,那就交给父容器的父容器......
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();
//Button处理事件后,调用addTouchTarget方法--->mFirstTouchTarget!=null--->newTouchTarget==mFirstTouchTarget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
这也就意味着,这个ViewGroup的多层子View们就不会有得到事件的机会了。从ViewGroup这就已经拦截了。
然后就往下走if,这里mFirstTouchTarget肯定为null,因为它在上面那一大段if里面的addTouchTarget方法内才被赋值。
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//这个while循环内的操作,意味着while只会进去一次。
while (target != null) {
//next为null
final TouchTarget next = target.next;
//alreadyDispatchedToNewTouchTarget就是true,上面刚刚给赋的值
//两个条件都满足,即handled=true;while只循环一次,故退出循环后,直接就将handled = true返回了。
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
//将null给到target
target = next;
}
}
还是会调用dispatchTransformedTouchEvent方法,但是参数有改变,child为null。这样就会走到dispatchTransformedTouchEvent方法的if中:
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
......
调用的是super的dispatchTouchEvent方法,并非else中的child.dispatchTouchEvent方法。即调用的是ViewGroup自己的dispatchTouchEvent方法。即这个事件由ViewGroup自己处理。这也是当重写的onInterceptTouchEvent方法返回true后,其包含的子View就不会滑动了的原因所在。
即父容器不进行拦截,让子View进行拦截。如果子View需要该事件,就消耗掉。否则,就还给父容器处理。和分发机制有点区别,内部拦截法需要requestDisallowInterceptTouchEvent方法的配合才行(上面已经结合源码解释过了),还得重写子View的分发方法dispatchTouchEvent方法:
//内部拦截法
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
//父容器需要该事件
if (Math.abs(deltaX) > Math.abs(deltaY)) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
除此之外,父容器也得默认拦截除DOWN外的其他事件,只有这样,当子View调用getParent().requestDisallowInterceptTouchEvent(false)时,父容器才会继续拦截所需的事件。上面提到过,DOWN事件受标志位FLAG_DISALLOW_INTERCEPT控制。一旦父容器拦截DOWN事件,所有的事件就都无法传递到子元素中去了,内部拦截法也就成了“黄粱一梦”。
即父容器进行拦截,如果父容器需要该事件就拦截,反之不拦截。外部拦截相比于内部拦截,更简单。外部拦截的做法,更符合事件分发机制。外部拦截法比较“省心”,只需要重写父容器的onInterceptTouchEvent方法即可:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// 外部拦截法
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastX = (int) event.getX();
mLastY = (int) event.getY();
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
return true;
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
return super.onInterceptTouchEvent(event);
}