本文背景:前些天用到了之前写的自定义图片文字复合控件,在给他设置监听时遇到了麻烦。虽然最后解决了问题,但发现在不重写LinearLayout的onInterceptTouchEvent时,子ImageView、子TextView、父Linearlayout三者不同的属性配置(android:clickable android:focuseable)会造成自定义控件onClick监听失败、或成功。复写了父Linearlayout 的onInterceptTouchEvent时,监听不受子图片、子文字的属性影响。为知其所以然,深入研究android的事件传递机制,记录于此。

一、View的dispatchTouchEvent和onTouchEvent

探讨Android事件传递机制前,明确android的两大基础控件类型:View和ViewGroup。View即普通的控件,没有子布局的,如Button、TextView. ViewGroup继承自View,表示可以有子控件,如Linearlayout、Listview这些。而事件即MotionEvent,最重要的有3个:(1)MotionEvent.ACTION_DOWN 按下View,是所有事件的开始(2)MotionEvent.ACTION_MOVE 滑动事件

(3)MotionEvent.ACTION_UP 与down对应,表示抬起

另外,明确事件传递机制的最终目的都是为了触发执行View的点击监听和触摸监听: ******.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

Log.i(tag, "testLinelayout---onClick...");

}

});

*******.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

return false;

}

});

我们简称为onClick监听和onTouch监听,一般程序会注册这两个监听。从上面可以看到,onTouch监听里默认return false。不要小看了这个return false,后面可以看到它有大用。 对于View来说,事件传递机制有两个函数:dispatchTouchEvent负责分发事件,在dispatch***里又会调用onTouchEvent表示执行事件,或者说消费事件,结合源码分析其流程。事件传递的入口是View的dispatchTouchEvent()函数:

/**

* 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) {

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onTouchEvent(event, 0);

}

if (onFilterTouchEventForSecurity(event)) {

//noinspection SimplifiableIfStatement

ListenerInfo li = mListenerInfo;

if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED

&& li.mOnTouchListener.onTouch(this, event)) {

return true;

}

if (onTouchEvent(event)) {

return true;

}

}

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);

}

return false;

}

找到这个判断: if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED

&& li.mOnTouchListener.onTouch(this, event)) {

return true;

}

他会执行View的OnTouchListener.onTouch这个函数,也就是上面说的onTouch监听。里面有三个判断,如果三个都为1,就会执行return true,不往下走了。而默认的onTouch监听返回false,只要一个是false,就不会返回true。接着往下看,程序执行onTouchEvent:

if (onTouchEvent(event)) {

return true;

}

onTouchEvent的源码比较多,贴最重要的:

if (!mHasPerformedLongPress) {

// This is a tap, so remove the longpress check

removeLongPressCallback();

// Only perform take click actions if we were in the pressed state

if (!focusTaken) {

// Use a Runnable and post this rather than calling

// performClick directly. This lets other visual state

// of the view update before click actions start.

if (mPerformClick == null) {

mPerformClick = new PerformClick();

}

if (!post(mPerformClick)) {

performClick();

}

}

}

可以看到有个performClick(),它的源码里有这么一句 li.mOnClickListener.onClick(this);

public boolean performClick() {

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

ListenerInfo li = mListenerInfo;

if (li != null && li.mOnClickListener != null) {

playSoundEffect(SoundEffectConstants.CLICK);

li.mOnClickListener.onClick(this);

return true;

}

return false;

}

终于对上了,它执行了我们注册的onClick监听。当然执行前会经过一系列判断,是否注册了监听等。

总结:1、事件入口是dispatchTouchEvent(),它会先执行注册的onTouch监听,如果一切顺利的话,接着执行onTouchEvent,在onTouchEvent里会执行onClick监听。2、无论是dispatchTouchEvent还是onTouchEvent,如果返回true表示这个事件已经被消费、处理了,不再往下传了。在dispathTouchEvent的源码里可以看到,如果onTouchEvent返回了true,那么它也返回true。如果dispatch***在执行onTouch监听的时候,onTouch返回了true,那么它也返回true,这个事件提前被onTouch消费掉了。就不再执行onTouchEvent了,更别说onClick监听了。3、我们通常在onTouch监听了设置图片一旦被触摸就改变它的背景、透明度之类的,这个onTouch表示事件的时机。而在onClick监听了去具体干某些事。

下面通过代码来说明,自定义一个TestButton继承自Button,重写它的dispath***和onTouchEvent方法,为了简单只关注down和up事件。

package org.yanzi.ui;

import android.content.Context;

import android.util.AttributeSet;

import android.util.Log;

import android.view.MotionEvent;

import android.widget.Button;

public class TestButton extends Button {

private final static String tag = "yan";

public TestButton(Context context, AttributeSet attrs) {

super(context, attrs);

// TODO Auto-generated constructor stub

}

@Override

public boolean onTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "TestButton-onTouchEvent-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "TestButton-onTouchEvent-ACTION_UP...");

break;

default:break;

}

return super.onTouchEvent(event);

}

@Override

public boolean dispatchTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "TestButton-dispatchTouchEvent-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "TestButton-dispatchTouchEvent-ACTION_UP...");

break;

default:break;

}

return super.dispatchTouchEvent(event);

}

}

在Activity里注册两个监听:

testBtn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

Log.i(tag, "testBtn---onClick...");

}

});

testBtn.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "testBtn-onTouch-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "testBtn-onTouch-ACTION_UP...");

break;

default:break;

}

return false;

}

});

同时复写Activity的dispatch方法和onTouchEvent方法:

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

// TODO Auto-generated method stub

switch(ev.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "MainActivity-dispatchTouchEvent-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "MainActivity-dispatchTouchEvent-ACTION_UP...");

break;

default:break;

}

return super.dispatchTouchEvent(ev);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "MainActivity-onTouchEvent-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "MainActivity-onTouchEvent-ACTION_UP...");

break;

default:break;

}

return super.onTouchEvent(event);

}

最终一次点击,打印信息如下: Line 33: 01-08 14:59:45.847 I/yan ( 4613): MainActivity-dispatchTouchEvent-ACTION_DOWN...

Line 35: 01-08 14:59:45.849 I/yan ( 4613): TestButton-dispatchTouchEvent-ACTION_DOWN...

Line 37: 01-08 14:59:45.849 I/yan ( 4613): testBtn-onTouch-ACTION_DOWN...

Line 39: 01-08 14:59:45.849 I/yan ( 4613): TestButton-onTouchEvent-ACTION_DOWN...

Line 41: 01-08 14:59:45.939 I/yan ( 4613): MainActivity-dispatchTouchEvent-ACTION_UP...

Line 43: 01-08 14:59:45.941 I/yan ( 4613): TestButton-dispatchTouchEvent-ACTION_UP...

Line 45: 01-08 14:59:45.944 I/yan ( 4613): testBtn-onTouch-ACTION_UP...

Line 47: 01-08 14:59:45.946 I/yan ( 4613): TestButton-onTouchEvent-ACTION_UP...

Line 49: 01-08 14:59:45.974 I/yan ( 4613): testBtn---onClick...

事件先由Activity的dispatchTouchEvent进行分发,然后TestButton的dispatchTouchEvent进行分发,接着执行onTouch监听,然后执行onTouchEvent。第二次UP动作的时候,在onTouchEvent里又执行了onClick监听。 如果我们想这个TestButton只能执行onTouch监听不能执行onClick监听,方法有很多。在onTouch监听里默认返回false改为true,如下:

testBtn.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "testBtn-onTouch-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "testBtn-onTouch-ACTION_UP...");

break;

default:break;

}

return true;

}

});

事件流程为: Line 75: 01-08 15:05:51.627 I/yan ( 5262): MainActivity-dispatchTouchEvent-ACTION_DOWN...

Line 77: 01-08 15:05:51.628 I/yan ( 5262): TestButton-dispatchTouchEvent-ACTION_DOWN...

Line 79: 01-08 15:05:51.629 I/yan ( 5262): testBtn-onTouch-ACTION_DOWN...

Line 81: 01-08 15:05:51.689 I/yan ( 5262): MainActivity-dispatchTouchEvent-ACTION_UP...

Line 83: 01-08 15:05:51.691 I/yan ( 5262): TestButton-dispatchTouchEvent-ACTION_UP...

Line 85: 01-08 15:05:51.695 I/yan ( 5262): testBtn-onTouch-ACTION_UP...

可以看到压根就没执行onTouchEvent。因为onTouch返回了true,已提前将这个事件消费了,就不往下传了,dispatch流程提前终止。

二、ViewGroup的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

再来看ViewGroup,在复写ViewGroup时可以发现它的onTouchEvent在在View里的,表示这两个方法是一样的。但dispatchTouchEvent是在ViewGroup里的,表示和View的dispatchTouchEvent不一样,多了一个onInterceptTouchEvent函数,表示拦截的意思。链接 打个很形象的比喻,这玩意就像个秘书、谋士。为啥View没有呢,因为它级别不够,一个Button里面是不可能有子View的。但LinearLayout(继承ViewGroup)就有孩子(子布局),这个onInterceptTouchEvent就会判断事件要不要通知它的孩子呢。它的源码如下:

public boolean dispatchTouchEvent(MotionEvent ev) {

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onTouchEvent(ev, 1);

}

boolean handled = false;

if (onFilterTouchEventForSecurity(ev)) {

final int action = ev.getAction();

final int actionMasked = action & MotionEvent.ACTION_MASK;

// Handle an initial down.

if (actionMasked == MotionEvent.ACTION_DOWN) {

// Throw away all previous state when starting a new touch gesture.

// The framework may have dropped the up or cancel event for the previous gesture

// due to an app switch, ANR, or some other state change.

cancelAndClearTouchTargets(ev);

resetTouchState();

}

// Check for interception.

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;

}

// Check for cancelation.

final boolean canceled = resetCancelNextUpFlag(this)

|| actionMasked == MotionEvent.ACTION_CANCEL;

// Update list of touch targets for pointer down, if needed.

final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;

TouchTarget newTouchTarget = null;

boolean alreadyDispatchedToNewTouchTarget = false;

if (!canceled && !intercepted) {

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;

if (childrenCount != 0) {

// Find a child that can receive the event.

// Scan children from front to back.

final View[] children = mChildren;

final float x = ev.getX(actionIndex);

final float y = ev.getY(actionIndex);

final boolean customOrder = isChildrenDrawingOrderEnabled();

for (int i = childrenCount - 1; i >= 0; i--) {

final int childIndex = customOrder ?

getChildDrawingOrder(childrenCount, i) : i;

final View child = children[childIndex];

if (!canViewReceivePointerEvents(child)

|| !isTransformedTouchPointInView(x, y, child, null)) {

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

if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

// Child wants to receive touch within its bounds.

mLastTouchDownTime = ev.getDownTime();

mLastTouchDownIndex = childIndex;

mLastTouchDownX = ev.getX();

mLastTouchDownY = ev.getY();

newTouchTarget = addTouchTarget(child, idBitsToAssign);

alreadyDispatchedToNewTouchTarget = true;

break;

}

}

}

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;

}

}

}

// Dispatch to touch targets.

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 (target != null) {

final TouchTarget next = target.next;

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;

target = next;

}

}

// Update list of touch targets for pointer up or cancel, if needed.

if (canceled

|| actionMasked == MotionEvent.ACTION_UP

|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

resetTouchState();

} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {

final int actionIndex = ev.getActionIndex();

final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);

removePointersFromTouchTargets(idBitsToRemove);

}

}

if (!handled && mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);

}

return handled;

}

可以看到标红的有两句(intercepted = onInterceptTouchEvent(ev); if (!canceled && !intercepted) ),它会先调用 intercepted = onInterceptTouchEvent(ev);然后通过if判断。

public boolean onInterceptTouchEvent(MotionEvent ev) {

return false;

}

它就一句话,默认false。也就是说这个谋士默认的意见是,永远不拦截!!!!只要有孩子,就交给孩子们处理吧。下面给出实例说明,新建TestLinearLayout继承自Linearlayout。

package org.yanzi.ui;

import android.content.Context;

import android.util.AttributeSet;

import android.util.Log;

import android.view.MotionEvent;

import android.widget.LinearLayout;

public class TestLinearLayout extends LinearLayout{

private final static String tag = "yan";

public TestLinearLayout(Context context, AttributeSet attrs) {

super(context, attrs);

// TODO Auto-generated constructor stub

}

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

// TODO Auto-generated method stub

switch(ev.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "TestLinearLayout-dispatchTouchEvent-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "TestLinearLayout-dispatchTouchEvent-ACTION_UP...");

break;

default:break;

}

return super.dispatchTouchEvent(ev);

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

// TODO Auto-generated method stub

switch(ev.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "TestLinearLayout-onInterceptTouchEvent-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "TestLinearLayout-onInterceptTouchEvent-ACTION_UP...");

break;

default:break;

}

return super.onInterceptTouchEvent(ev);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "TestLinearLayout-onTouchEvent-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "TestLinearLayout-onTouchEvent-ACTION_UP...");

break;

default:break;

}

return super.onTouchEvent(event);

}

}

布局文件改成:

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=".MainActivity" >

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/hello_world" />

android:id="@+id/linearlayout_test"

android:layout_width="200dip"

android:layout_height="200dip" >

android:id="@+id/btn_test"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="测试按钮" />

在Activity里给这个自定义LinearLayout也注册上onClick监听、onTouch监听。

testLinelayout = (TestLinearLayout)findViewById(R.id.linearlayout_test);

testLinelayout.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

Log.i(tag, "testLinelayout-onTouch-ACTION_DOWN...");

break;

case MotionEvent.ACTION_UP:

Log.i(tag, "testLinelayout-onTouch-ACTION_UP...");

break;

default:break;

}

return false;

}

});

testLinelayout.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

Log.i(tag, "testLinelayout---onClick...");

}

});

不复写事件传递里的 任何方法,流程如下: Line 57: 01-08 15:29:42.167 I/yan ( 5826): MainActivity-dispatchTouchEvent-ACTION_DOWN...

Line 59: 01-08 15:29:42.169 I/yan ( 5826): TestLinearLayout-dispatchTouchEvent-ACTION_DOWN...

Line 61: 01-08 15:29:42.169 I/yan ( 5826): TestLinearLayout-onInterceptTouchEvent-ACTION_DOWN...

Line 63: 01-08 15:29:42.169 I/yan ( 5826): TestButton-dispatchTouchEvent-ACTION_DOWN...

Line 65: 01-08 15:29:42.170 I/yan ( 5826): testBtn-onTouch-ACTION_DOWN...

Line 67: 01-08 15:29:42.170 I/yan ( 5826): TestButton-onTouchEvent-ACTION_DOWN...---------------------------------------------------------------------------------------------------------------------------

Line 69: 01-08 15:29:42.279 I/yan ( 5826): MainActivity-dispatchTouchEvent-ACTION_UP...

Line 71: 01-08 15:29:42.280 I/yan ( 5826): TestLinearLayout-dispatchTouchEvent-ACTION_UP...

Line 73: 01-08 15:29:42.283 I/yan ( 5826): TestLinearLayout-onInterceptTouchEvent-ACTION_UP...

Line 75: 01-08 15:29:42.287 I/yan ( 5826): TestButton-dispatchTouchEvent-ACTION_UP...

Line 81: 01-08 15:29:42.298 I/yan ( 5826): testBtn-onTouch-ACTION_UP...

Line 83: 01-08 15:29:42.301 I/yan ( 5826): TestButton-onTouchEvent-ACTION_UP...

Line 85: 01-08 15:29:42.313 I/yan ( 5826): testBtn---onClick...

由Activity的dispatchTouchEvent----Linearlayout的dispatchTouchEvent------------问问它的谋士要不要让孩子知道onInterceptTouchEvent---------孩子的dispatchTouchEvent-----孩子的onTouch监听------孩子的onTouchEvent----孩子的onClick监听。为了更清晰这个流程,下面作如下改动:1、如果事件传给了孩子们,但孩子没有onTouch和onClick监听怎么办?即将button的onclick和onTouch都注释掉:流程如下: Line 131: 01-08 15:36:16.574 I/yan ( 6124): TestLinearLayout-dispatchTouchEvent-ACTION_DOWN...

Line 133: 01-08 15:36:16.574 I/yan ( 6124): TestLinearLayout-onInterceptTouchEvent-ACTION_DOWN...

Line 135: 01-08 15:36:16.574 I/yan ( 6124): TestButton-dispatchTouchEvent-ACTION_DOWN...

Line 137: 01-08 15:36:16.575 I/yan ( 6124): TestButton-onTouchEvent-ACTION_DOWN...

Line 143: 01-08 15:36:16.746 I/yan ( 6124): MainActivity-dispatchTouchEvent-ACTION_UP...

Line 145: 01-08 15:36:16.747 I/yan ( 6124): TestLinearLayout-dispatchTouchEvent-ACTION_UP...

Line 147: 01-08 15:36:16.747 I/yan ( 6124): TestLinearLayout-onInterceptTouchEvent-ACTION_UP...

Line 149: 01-08 15:36:16.748 I/yan ( 6124): TestButton-dispatchTouchEvent-ACTION_UP...

Line 151: 01-08 15:36:16.748 I/yan ( 6124): TestButton-onTouchEvent-ACTION_UP...

因为事件给了孩子们,它没监听也关系不到父亲了,父亲的onClick和onTouch都没执行。2,如果将TestLinearlayout的onInterceptTouchEvent 改成return true,即不让孩子们知道。 Line 57: 01-08 15:40:06.832 I/yan ( 6640): MainActivity-dispatchTouchEvent-ACTION_DOWN...

Line 59: 01-08 15:40:06.835 I/yan ( 6640): TestLinearLayout-dispatchTouchEvent-ACTION_DOWN...

Line 61: 01-08 15:40:06.836 I/yan ( 6640): TestLinearLayout-onInterceptTouchEvent-ACTION_DOWN...

Line 63: 01-08 15:40:06.836 I/yan ( 6640): testLinelayout-onTouch-ACTION_DOWN...

Line 65: 01-08 15:40:06.836 I/yan ( 6640): TestLinearLayout-onTouchEvent-ACTION_DOWN...

Line 67: 01-08 15:40:07.016 I/yan ( 6640): MainActivity-dispatchTouchEvent-ACTION_UP...

Line 69: 01-08 15:40:07.017 I/yan ( 6640): TestLinearLayout-dispatchTouchEvent-ACTION_UP...

Line 73: 01-08 15:40:07.025 I/yan ( 6640): testLinelayout-onTouch-ACTION_UP...

Line 75: 01-08 15:40:07.026 I/yan ( 6640): TestLinearLayout-onTouchEvent-ACTION_UP...

Line 77: 01-08 15:40:07.052 I/yan ( 6640): testLinelayout---onClick...

果然事件就此打住,孩子们压根不知道,父亲执行了onClick和onTouch监听。可见父亲还是伟大的啊,只要谋士不拦截事件,那么事件就给孩子。 最后的结论:1、如果是自定义复合控件,如图片+文字,我再Activity里给你注册了onClick监听,期望点击它执行。那么最简单的方法就是将图片+文字的父布局,也即让其容器ViewGroup的秘书将事件拦下,这样父亲就可以执行onClick了。这时候的父亲就像一个独立的孩子一样了(View),无官一身轻,再也不用管它的孩子了,可以正常onClick onTouch.2、如果希望一个View只onTouch而不onClick,在onTouch里return true就ok了。3、dispatch是为了onTouch监听,onTouchEvent是为了onClick监听。4、自定义布局时,一般情况下:@Override

public boolean onTouchEvent(MotionEvent event) {return super.onTouchEvent(event);}

@Override

public boolean dispatchTouchEvent(MotionEvent event) {return super.dispatchTouchEvent(event);

我们可以复写,但是最后的super.***是万万不能少滴。如果少了,表示连dispatch*** onTouchEvent压根就不调用了,事件就此打住。 貌似真相水落石出了,但究竟清楚了没有请看下篇根据自定义复合控件的监听问题再探讨下。

参考: 链接1 (灰常感谢前辈的大作啊) 链接2测试代码: http://www.it165.net/uploadfile/files/2014/0403/Androidsjcd.zip

android touch机制,细说Android事件传递机制(dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent)...相关推荐

  1. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 四 | View 事件传递机制 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  2. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  3. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 六 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  4. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  5. Android 事件传递机制总结

    Android 事件传递机制总结 Android View虽然不是四大组件,但是其重要程度堪比四大组件.初级工程师到中高级工程师,这些都是很重要的,因为事件分发机制面试也会经常被提及,如果我们能get ...

  6. Android 中Touch(触屏)事件传递机制

    版本:2.0 日期:2014.3.21 2014.3.29 版权:© 2014 kince 转载注明出处 一.基本概念 在实际开发中,经常会遇到与触屏事件有关的问题,最典型的一个就是滑动冲突.比如在使 ...

  7. Android Touch事件传递机制 二:单纯的(伪生命周期) 这个清楚一点

    转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...

  8. Android Touch事件传递机制 二:单纯的(伪生命周期)

    转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...

  9. android touch事件坐标原点,图解Android:Touch事件传递机制

    前言 Android事件管理机制是一名专业Android研发工程师必须要了解的核心知识之一,深入了解一下该机制无论对我们日常开发还是找工作,乃至于对我们的架构思想都有很大的帮助.Android中我们用 ...

最新文章

  1. codevs 1002 搭桥
  2. 拆解 Linux 网络包发送过程
  3. 让小程序在自有App中启动的技术来了:mPaaS小程序架构深度解析
  4. Python中利用plt显示中文标题解决方案
  5. ux设计师怎样找同类产品_没有预算? 别找借口。 便宜的UX上的UX 2:让我们开始构建。...
  6. php制作后台驻留执行 ,同时提前返回逻辑信息进行判断的实例
  7. LQR轨迹跟踪算法Python/Matlab算法实现_代码(2)
  8. 一起来学习丨听海华大赛第一名团队聊比赛经验和心得
  9. csuoj 1355: 地雷清除计划
  10. 迁移学习全面指南:概念、应用、优势、挑战
  11. linux切换到其他节点,linux 怎么切换节点
  12. 《数字信号处理》——(一).DTFT、DFT(python实现)
  13. 第九讲 函数间接展开成幂级数
  14. 《业务测试》手机号码格式
  15. android robotium测试,Android robotium自动化测试
  16. HTB-Antique
  17. XCOM 导入条目显示正在使用中解决方案
  18. mysql count忽略空_MySQL count 过滤空值,使其不将空数据计入条数
  19. COCOS学习笔记--TexturePacker使用详解
  20. python星座分析

热门文章

  1. spoj 2916. Can you answer these queries V(线段树)
  2. 为什么河文档是黑人,而小河看起来却是个白人
  3. visual studio 2019 相关配置选项
  4. [533]python获取微信好友头像生成点阵图片
  5. Google Earth Engine(GEE)——一个免费下载Landsat影像的APP
  6. 《代码规范》如何写出干净的代码(四)对象和类
  7. 「智能交通技术」知识星球内容介绍
  8. ANSYS Workbench接触设置—对称和非对称接触
  9. 图片设计素材网址大全
  10. 职场中生存的头等大事:保持尊重