您的位置:首页 > 移动开发 > Android开发

android的touch事件分发机制-View篇

2015-08-11 13:33 645 查看
转载请注明出处:http://write.blog.csdn.net/mdeditor#!postId=47418837

先赞一下Markdown编辑器,很好用:)

网上讲解touch事件分发的文章已经很多了,看得多了,更觉得混乱。所以打算自己写一篇,顺理一下,也希望对后来者能有点用处。

在具体讲解之前,我想先回答几个常见的问题。

1. 为什么我们要了解touch的分发机制

2. touch的事件分发涉及到哪几个方法

3. view和viewgroup的分发机制一样吗

我们先来看一段代码:

TextView view = (TextView)findViewById(R.id.view_touch);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(null, "OnClickListener");
}
});
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.e(null, "OnTouchListener action=" + motionEvent.getAction());
return true;
}
});


在这段代码里,我声明了一个TextView,然后注册了一个新的touch事件的监听器,运行结果如下:

08-11 16:14:16.380 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=0

08-11 16:14:16.575 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=1

08-11 16:14:16.600 27057-27057/wanmei.com.svgtest E/﹕ OnClickListener

这个比较简单,我想大家都知道:

action=0指的是ACTION_DOWN事件,action=1指的是ACTION_UP事件,因为我点击的比较快,手指也没有移动,所以并没有触发ACTION_MOVE事件(action=2)

而Click事件在touch事件之后执行了

现在,我们把onTouch方法的返回值改成true,输出会怎么样呢:

08-11 16:14:16.380 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=0

08-11 16:14:16.575 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=1

可以看到,在触发了touch事件后,并没有触发Click事件.

这是为啥?

先普及一个问题:在你点击一个view的时候,其实并不是直接就走onTouch了(虽然看起来是你注册了一个touch的监听器并实现了这个接口)。

实际上,在你点击以后,先走的是dispatchPointerEvent这个方法(此方法在view类里可以看到):

public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}


这个方法具体干啥用的,有兴趣的童鞋可以研究一下,因为不在咱们这篇的研究范围内,就不赘述了。

但是大家可以看到,这个方法的return值有两种,当event.isTouchEvent()的时候,调用了dispatchTouchEvent方法。

这个dispatchTouchEvent方法才是咱们关心的。它负责当view捕获到touch事件时,分发事件:

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)) {
//noinspection SimplifiableIfStatement
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;
}
}

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;
}


代码比较长,但是咱们不需要都看明白,单独看下面这段:

if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
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;
}
}


我来解释一下:

首先通过onFilterTouchEventForSecurity判断当前view是否被遮挡,然后定义了一个ListenerInfo的实例。

ListenerInfo这个类是View的内部类,有兴趣的童鞋可以看看源码,里面定义了各种监听器,包括onclick,onlongclick什么的。

然后重点来了,在判断了一堆是不是null,可不可用之后,最终还是调用到了li.mOnTouchListener.onTouch(this, event)

也就是我们在文章最初提到的onTouch方法。

而且,在这里我们还用到这个onTouch的返回值(这里其实用到了4个返回值,li肯定是不为null了,li.mOnTouchListener是不是null取决于你有没有setOnTouchListener,(mViewFlags & ENABLED_MASK) == ENABLED取决于你的这个view控件是不是enable的),只有当onTouch返回true的时候,result才等于true,也就是dispatchTouchEvent才返回true。

那么dispatchTouchEvent返回true or false有什么意义呢?咱们来看源码的注释:

@return True if the event was handled by the view, false otherwise.

当返回true的时候,表明事件已经被view被处理掉了,false的时候反之。

我们来理一下思路:

当你触摸屏幕的时候,view最先做的是分发这个触摸事件,所以不管咋样,进来给dispatchTouchEvent分发,然后呢,在做了一堆判断后,分发给了onTouch。

而onTouch在做了处理之后,返回了一个布尔值。这个布尔值将决定dispatchTouchEvent是否继续分发这个触摸事件。

所以在文章的开头,如果你的onTouch返回了true,明显dispatchTouchEvent就不会再分发给onClick了

ps:dispatchTouchEvent代码里还有一个onTouchEvent,这个方法是activity的,有机会我会写一篇单独说明。

pps:写的比较浅,看到csdn里有很多写的比我深的多的,只希望我这篇对于初学者更易懂一些:)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: