Android Touch事件传递机制引发的血案
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38942135
关于Android Touch事件传递机制我之前也写过两篇文章,自觉得对Touche事件还是理解得比較清楚的,可是近期遇到的一个问题,让我再次对Android Touche事件进行一次学习。
我的关于Android Touche事件传递机制的文章例如以下:
http://blog.csdn.net/yuanzeyao/article/details/37961997
http://blog.csdn.net/yuanzeyao/article/details/38025165
我在这两篇文章中得出过下面结论:
1、假设一个view是clickable的,那么这个View的onTouchEvent是一定会返回true的,也就是说不论什么触摸事件都会被消费掉
2、假设一个View对于ACTION_DOWN事件没有消费掉(onTouchEvent 返回false),那么兴许的ACTION_MOVE,ACTION_UP是都不会接受到的,也就是没有机会处理这些事件,这些事件都是在父View里面给处理了
3、假设一个ViewGroup想要拦截事件(不让事件传递到子View),那么它只须要改写ViewGroup的onInterceptTouchEvent(MotionEvent ev) 方法,让他返回true,或者调用requestDisallowInterceptTouchEvent(true);
4、Android中的Touche事件是从底层向上层传递的 Activity->DecorView->ViewGroup->View
理解了上面的问题,我们就開始看看我所遇到的问题吧,
在使用SlideMenu的时候,在中的Activity中只放置一个TextView,你会发现SlideMenu无法滑动,当时通过顶部的Title能够滑动,因为对SlideMenu用的不是非常熟,当时以为是SlideMenu的哪个属性用错了,后来一直没有解决这个问题,直到一位网友说设置TextView的clickable为true就能够解决这个问题,我尝试了一下,还真行!哈哈。。。,这个里面的原因你理解了吗?假设没有理解,请继续往下看
依照我之前对Touche事件的理解,假设设置clickable,那么Touche事件肯定就被TextView给消费掉了,假设被TextView消费掉了,那么SlideMenu怎样实现滑动?要解开这个问题答案,还是看看SlideMenu的源代码吗
我们首先看看SlideMenu中CustomViewAbove和Touche有关的方法
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) { if (!mEnabled)
return false; final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; if (action == MotionEvent.ACTION_DOWN && DEBUG)
Log.v(TAG, "Received ACTION_DOWN"); if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP
|| (action != MotionEvent.ACTION_DOWN && mIsUnableToDrag)) {
endDrag();
return false;
} switch (action) {
case MotionEvent.ACTION_MOVE:
try{
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER)
break;
final int pointerIndex = this.getPointerIndex(ev, activePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float dx = x - mLastMotionX;
final float xDiff = Math.abs(dx);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) Log.v(TAG, "onInterceptTouch moved to:(" + x + ", " + y + "), diff:(" + xDiff + ", " + yDiff + "), mLastMotionX:" + mLastMotionX);
if (xDiff > mTouchSlop && xDiff > yDiff && thisSlideAllowed(dx)) {
if (DEBUG) Log.v(TAG, "Starting drag! from onInterceptTouch");
startDrag();
mLastMotionX = x;
setScrollingCacheEnabled(true);
} else if (yDiff > mTouchSlop) {
mIsUnableToDrag = true;
}
}
catch(IllegalArgumentException e)
{
e.printStackTrace();
}
break; case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getAction() & ((Build.VERSION.SDK_INT >= 8) ? MotionEvent.ACTION_POINTER_INDEX_MASK :
MotionEvent.ACTION_POINTER_INDEX_MASK);
mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, mActivePointerId);
mLastMotionY = MotionEventCompat.getY(ev, mActivePointerId);
if (thisTouchAllowed(ev)) {
mIsBeingDragged = false;
mIsUnableToDrag = false;
if (isMenuOpen() && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {
mQuickReturn = true;
}
} else {
mIsUnableToDrag = true;
}
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
} if (!mIsBeingDragged) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
}
return mIsBeingDragged || mQuickReturn;
}
看看这种方法,这种方法里面有个逻辑就是当滑动到一定距离,就会返回true,也就是说会拦截滑动事件,第一个ACTION_DOWN肯定不会拦截。
再看看onToucheEvent.java
@Override
public boolean onTouchEvent(MotionEvent ev) { if (!mEnabled)
return false; // if (!mIsBeingDragged && !thisTouchAllowed(ev))
// return false; if (!mIsBeingDragged && !mQuickReturn)
return false; final int action = ev.getAction(); if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev); switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
completeScroll(); // Remember where the motion event started
mLastMotionX = mInitialMotionX = ev.getX();
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
if (mActivePointerId == INVALID_POINTER)
break;
final int pointerIndex = getPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float dx = x - mLastMotionX;
final float xDiff = Math.abs(dx);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) Log.v(TAG, "onTouch moved to:(" + x + ", " + y + "), diff:(" + xDiff + ", " + yDiff + ")\nmIsBeingDragged:" + mIsBeingDragged + ", mLastMotionX:" + mLastMotionX);
if ((xDiff > mTouchSlop || (mQuickReturn && xDiff > mTouchSlop / 4))
&& xDiff > yDiff && thisSlideAllowed(dx)) {
if (DEBUG) Log.v(TAG, "Starting drag! from onTouch");
startDrag();
mLastMotionX = x;
setScrollingCacheEnabled(true);
} else {
if (DEBUG) Log.v(TAG, "onTouch returning false");
return false;
}
}
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId == INVALID_POINTER) {
break;
}
final float x = MotionEventCompat.getX(ev, activePointerIndex);
final float deltaX = mLastMotionX - x;
mLastMotionX = x;
float oldScrollX = getScrollX();
float scrollX = oldScrollX + deltaX;
final float leftBound = getLeftBound();
final float rightBound = getRightBound();
if (scrollX < leftBound) {
scrollX = leftBound;
} else if (scrollX > rightBound) {
scrollX = rightBound;
}
// Don't lose the rounded component
mLastMotionX += scrollX - (int) scrollX;
scrollTo((int) scrollX, getScrollY());
pageScrolled((int) scrollX);
}
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
velocityTracker, mActivePointerId);
final int scrollX = getScrollX();
// final int widthWithMargin = getWidth();
// final float pageOffset = (float) (scrollX % widthWithMargin) / widthWithMargin;
// TODO test this. should get better flinging behavior
final float pageOffset = (float) (scrollX - getDestScrollX(mCurItem)) / getBehindWidth();
final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId != INVALID_POINTER) {
final float x = MotionEventCompat.getX(ev, activePointerIndex);
final int totalDelta = (int) (x - mInitialMotionX);
int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);
setCurrentItemInternal(nextPage, true, true, initialVelocity);
} else {
setCurrentItemInternal(mCurItem, true, true, initialVelocity);
}
mActivePointerId = INVALID_POINTER;
endDrag();
} else if (mQuickReturn && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {
// close the menu
setCurrentItem(1);
endDrag();
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
setCurrentItemInternal(mCurItem, true, true);
mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int index = MotionEventCompat.getActionIndex(ev);
final float x = MotionEventCompat.getX(ev, index);
mLastMotionX = x;
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
int pointerIndex = this.getPointerIndex(ev, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionX = MotionEventCompat.getX(ev, pointerIndex);
break;
}
return true;
}
我们重点观察ACTION_DWON事件,对于ACTION_DWON事件,SlideMenu是没有拦截的,所以传递到了TextView,因为默认TextView是没有clickable的,所以是不会消费这个事件,假设TextView不消费,那么事件就传递到了SlideMenu,可是我们发如今SlideMenu中也没有消费这个事件,还记得我们上面的结论2吗,依据结论2,我们知道后面的事件是传递只是来的,所以导致了SlideMenu无法滑动。
假设我们设置了clickable,那么第一个ACTION_DOWN就被TextView处理了,所以后面每一个事件都会传递到TextView(前提是不被拦截,实际结果是被拦截,并被SlideMenu处理,所以SlideMenu滑动了)
Android Touch事件传递机制引发的血案的更多相关文章
- Android Touch事件传递机制 二:单纯的(伪生命周期)
转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...
- Android Touch事件传递机制 一: OnTouch,OnItemClick(监听器),dispatchTouchEvent(伪生命周期)
ViewGroup View Activity dispatchTouchEvent 有 有 有 onInterceptTouchEvent 有 无 无 onTouchEvent 有 有 有 例 ...
- Android touch 事件传递机制
前言: (1)在自定义view的时候经常会遇到事件拦截处理,比如在侧滑菜单的时候,我们希望在侧滑菜单里面有listview控件,但是我们希望既能左右滑动又能上下滑动,这个时候就需要对触摸的touch事 ...
- Android Touch事件传递机制通俗讲解
在讲正题之前我们讲一段有关任务传递的小故事,抛砖迎玉下: 话说一家软件公司,来一个任务,分派给了开发经理去完成: 开发经理拿到,看了一下,感觉好简单,于是 开发经理:分派给了开发组长 开发组长:分派给 ...
- Android Touch事件传递机制 二:单纯的(伪生命周期) 这个清楚一点
转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...
- Android Touch事件传递机制详解 下
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165 资源下载:http://download.csdn.net/detail/yu ...
- Android Touch事件传递机制具体解释 下
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165 资源下载:http://download.csdn.net/detail/yu ...
- (转)Android Touch事件传递机制
-----来源:http://www.trinea.cn/android/touch-event-delivery-mechanism/ 介绍Android Touch事件的传递机制. 不少朋友私信问 ...
- Android Touch事件传递机制具体解释 上
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/37961997 近期总是遇到关于Android Touch事件的问题,如:滑动冲突的问题,曾经 ...
随机推荐
- 树形dp入门(poj 2342 Anniversary party)
题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上司,现在已知每个人的活跃指数和上司关系(当然不可能存在环),求邀请哪些人(多少人)来能使得晚 ...
- luogu P1353 [USACO08JAN]跑步Running
题目描述 The cows are trying to become better athletes, so Bessie is running on a track for exactly N (1 ...
- [BZOJ 2427] 软件安装
Link: BZOJ 2427 传送门 Solution: 只看样例的话会以为是裸的树形$dp$…… 但实际上题目并没有说明恰好仅有一个物品没有依赖项 因此原图可能由是由多棵树与多个图组成的 先跑一遍 ...
- 「PKUSC2018」星际穿越 (70分做法)
5371: [Pkusc2018]星际穿越 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 27 Solved: 11[Submit][Status] ...
- 【hdu1280】前M大的数
前m大的数 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submi ...
- 【动态规划】POJ3280- Cheapest Palindrome
[题目大意] 给出一个字符串,可以删除或添加一些字符,它们各自会消耗价值.问最少消耗多少价值,可以使得字符串变成回文的. [思路] 事实上删除或添加字符的价值只需要保持较小的那一个.假设当前要将(j, ...
- SourceTree运行慢的解决方案
以下两个Git命令可以解决SourceTree运行慢: git gc git prune 可以在SourceTree点击命令行模式打开Git命令行窗口输入,等待片刻执行完成,SourceTree的运行 ...
- Java学习笔记(7)
构造函数: 构造函数的作用:给对应的对象进行初始化. 构造函数定义的格式: 修饰符 函数名(形式参数){ 函数体... } 构造函数要注意的细节: 构造函数是没有返回值类型的 构造函数的函数名必须 ...
- [HTML/CSS]div显示在object、embed之上~
引言 帮一个朋友弄前端布局,一切都正常,但是嵌入object之后,div总是在object的下面,就上网找了一下解决方案,这里记录一下,好像只对flash有效. 用embed插入一个flash(比如优 ...
- 解决新浪微博API调用限制 突破rate_limit_status瓶颈
新浪微博开放平台API的调用和TWITTER接口一样,都是受限的,以小时为单位进行限定. 他有两个限制原则 1.用户不登录基于IP的限制,每小时1000次 2.用户登录了基于用户的限制,每小时1000 ...