Android[学习] ViewGroup和View的传递事件

最近项目中遇到了几种自定义View的覆盖使用,导致事件传递混了,查到了相关资料,最后自己做一个总结,留作记忆

  1. http://blog.csdn.net/yanzi1225627/article/details/22592831
  2. http://blog.csdn.net/xyz_lmn/article/details/12517911
  3. http://www.cnblogs.com/xiaoQLu/archive/2012/04/28/2474443.html
  4. http://ryantang.me/blog/2014/01/02/android-event-dispatch/
  5. http://codekk.com/open-source-project-analysis,

第一节 事件构成

在Android中,事件主要包括点按、长按、拖拽、滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作。所有这些都构成了Android中得事件响应。总的来说,所有的事件都由如下三个部分作为基础:

  • 按下(ACTION_DOWN)
  • 移动(ACTION_MOVE)
  • 抬起(ACTION_UP)

所有的操作事件首先必须执行的是按下操作(ACTION_DOWN),之后所有的操作都是以按下操作作为前提,当按下操作完成后,接下来可能是一段移动(ACTION_MOVE),然后抬起(ACTION_UP),或者是按下操作执行完成后没有移动就直接抬起。这一系列的动作在Android中都可以进行控制。

在Android中,所有的事件都是通过触摸屏上,而在屏蔽上交互的都是各种各样的视图组件Vew,而这些视图组件都继承于View,ViewGroup也继承View。

View主要分为:UI控件(继承View)和布局控件(继承ViewGroup)
UI控件:button,TextView
布局控件:RelativeLayout,容器控件

View 和 ViewGroup 有哪些区别呢
(1)View里,有两个回调函数 :

public boolean dispatchTouchEvent(MotionEvent ev);  
public boolean onTouchEvent(MotionEvent ev); 

(2)ViewGroup里,有三个回调函数 :

public boolean dispatchTouchEvent(MotionEvent ev);  
public boolean onInterceptTouchEvent(MotionEvent ev);  
public boolean onTouchEvent(MotionEvent ev);

注意:在Activity中和View一样,有二个回调函数

在View和ViewGroup中都存在dispatchTouchEvent和onTouchEvent方法,但是在ViewGroup中还有一个onInterceptTouchEvent方法,那这些方法都是干嘛的呢?别急,我们先看看他们的返回值。这些方法的返回值全部都是boolean型,为什么是boolean型呢,看看本文的标题,“事件传递”,传递的过程就是一个接一个,那到了某一个点后是否要继续往下传递呢?你发现了吗,“是否”二字就决定了这些方法应该用boolean来作为返回值。没错,这些方法都返回true或者是false。在Android中,所有的事件都是从开始经过传递到完成事件的消费,这些方法的返回值就决定了某一事件是否是继续往下传,还是被拦截了,或是被消费了。

接下来就是这些方法的参数,都接受了一个MotionEvent类型的参数,MotionEvent继承于InputEvent,用于标记各种动作事件。之前提到的ACTION_DOWN、ACTION_MOVE、ACTION_UP都是MotinEvent中定义的常量。我们通过MotionEvent传进来的事件类型来判断接收的是哪一种类型的事件。到现在,这三个方法的返回值和参数你应该都明白了,接下来就解释一下这三个方法分别在什么时候处理事件。

  • dispatchTouchEvent方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。返回true表示不继续分发,事件没有被消费。返回false则继续往下分发,如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截该事件。
  • onTouchEvent方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。
  • onInterceptTouchEvent是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能还有子View,而在Android中View中是不能再包含子View的(iOS可以)。

#第二节 事件传递

Android默认情况下都是由Activity的VewGroup开始传递的,从Activity到父级布局,在从父级布局到子布局,以此类推逐层传递。
如下图所示
从Activity到view的事件传递


触摸事件是一连串ACTION_DOWN,ACTION_MOVE…MOVE…MOVE、最后ACTION_UP,触摸事件还有ACTION_CANCEL事件。事件都是从ACTION_DOWN开始的,Activity的dispatchTouchEvent()首先接收到ACTION_DOWN,执行super.dispatchTouchEvent(ev),事件向下分发。

事件传递过程


如果View的ACTION_DOWN消费了,则其他的ACTION_MOVE…MOVE…MOVE、最后ACTION_UP,当传递到这里时也不会向上传递了。
事件消费


如果在第二个ViewGroup里面拦截ACTION_DOWN事件,则不会传递给下层的View,只是会传递一个ACTION_CANCEL事件。则其他的ACTION_MOVE…MOVE…MOVE、最后ACTION_UP事件,不会再传递给View。
事件拦截


android中的Touch事件都是从ACTION_DOWN开始的:
单手指操作:ACTION_DOWN—ACTION_MOVE----ACTION_UP
多手指操作:ACTION_DOWN—ACTION_POINTER_DOWN—ACTION_MOVE–ACTION_POINTER_UP—ACTION_UP.

#第三节


(1)View的dispatchTouchEvent和onTouchEvent

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 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 (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }

分析得:先执行View的OnTouchListener.onTouch这个函数,也就是上面说的onTouch监听。里面有三个判断,如果三个都为1,就会执行return true,不往下走了。而默认的onTouch监听返回false,只要一个是false,就不会返回true
然后再看onTouchEvent(event):

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

/**
     * Call this view's OnClickListener, if it is defined.  Performs all normal
     * actions associated with clicking: reporting accessibility event, playing
     * a sound, etc.
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean performClick() {
        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);
        return result;
    }

终于对上了,它执行了我们注册的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事件。

public class TestButton extends Button {

    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(ViewActivity1.tag, "TestButton-onTouchEvent-ACTION_DOWN...");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(ViewActivity1.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(ViewActivity1.tag, "TestButton-dispatchTouchEvent-ACTION_DOWN...");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(ViewActivity1.tag, "TestButton-dispatchTouchEvent-ACTION_UP...");
                break;
            default:break;
        }

        return super.dispatchTouchEvent(event);
    }
}

Activity里面复写dispatchTouchEvent和onTouchEvent,testBtn设置OnClick监听和OnTouch监听。

public class ViewActivity1 extends AppCompatActivity {

    public final static String tag = "gyh";

    private Button testBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view1);
        testBtn = (Button) findViewById(R.id.btn_test);
        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;
            }
        });
    }

    public boolean dispatchTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.i(tag, "ViewActivity1-dispatchTouchEvent-ACTION_DOWN...");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(tag, "ViewActivity1-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, "ViewActivity1-onTouchEvent-ACTION_DOWN...");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(tag, "ViewActivity1-onTouchEvent-ACTION_UP...");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        return super.onOptionsItemSelected(item);
    }
}

最终一次点击,打印信息如下:
08-20 11:38:54.576 514-514/com.ouou.demo.myview I/gyh﹕ ViewActivity1-dispatchTouchEvent-ACTION_DOWN…
08-20 11:38:54.577 514-514/com.ouou.demo.myview I/gyh﹕ TestButton-dispatchTouchEvent-ACTION_DOWN…
08-20 11:38:54.577 514-514/com.ouou.demo.myview I/gyh﹕ testBtn-onTouch-ACTION_DOWN…
08-20 11:38:54.577 514-514/com.ouou.demo.myview I/gyh﹕ TestButton-onTouchEvent-ACTION_DOWN…
08-20 11:38:54.650 514-514/com.ouou.demo.myview I/gyh﹕ ViewActivity1-dispatchTouchEvent-ACTION_UP…
08-20 11:38:54.650 514-514/com.ouou.demo.myview I/gyh﹕ TestButton-dispatchTouchEvent-ACTION_UP…
08-20 11:38:54.650 514-514/com.ouou.demo.myview I/gyh﹕ testBtn-onTouch-ACTION_UP…
08-20 11:38:54.650 514-514/com.ouou.demo.myview I/gyh﹕ TestButton-onTouchEvent-ACTION_UP…
08-20 11:38:54.651 514-514/com.ouou.demo.myview I/gyh﹕ 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;
            }
        });

打印如下:
08-20 11:40:18.422 5282-5282/com.ouou.demo.myview I/gyh﹕ ViewActivity1-dispatchTouchEvent-ACTION_DOWN…
08-20 11:40:18.422 5282-5282/com.ouou.demo.myview I/gyh﹕ TestButton-dispatchTouchEvent-ACTION_DOWN…
08-20 11:40:18.422 5282-5282/com.ouou.demo.myview I/gyh﹕ testBtn-onTouch-ACTION_DOWN…
08-20 11:40:18.495 5282-5282/com.ouou.demo.myview I/gyh﹕ ViewActivity1-dispatchTouchEvent-ACTION_UP…
08-20 11:40:18.496 5282-5282/com.ouou.demo.myview I/gyh﹕ TestButton-dispatchTouchEvent-ACTION_UP…
08-20 11:40:18.496 5282-5282/com.ouou.demo.myview I/gyh﹕ testBtn-onTouch-ACTION_UP…
(2)ViewGroup的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
再来看ViewGroup,在复写ViewGroup时可以发现它的onTouchEvent在在View里的,表示这两个方法是一样的。但dispatchTouchEvent是在ViewGroup里的,表示和View的dispatchTouchEvent不一样,多了一个onInterceptTouchEvent函数,表示拦截的意思。链接 打个很形象的比喻,这玩意就像个秘书、谋士。为啥View没有呢,因为它级别不够,一个Button里面是不可能有子View的。但LinearLayout(继承ViewGroup)就有孩子(子布局),这个onInterceptTouchEvent就会判断事件要不要通知它的孩子呢。它的源码如下:代码太多只写重要的部分

if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }

可以看到判断是否拦截onInterceptTouchEvent,看onInterceptTouchEvent里面的代码

public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

它就一句话,默认false。也就是说这个谋士默认的意见是,永远不拦截!!!!只要有孩子,就交给孩子们处理吧。

下面是TestLinearLayout实例

public class TestLinearLayout extends LinearLayout{

    public final static String tag = "gyh";

    public TestLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

布局是这样的

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".DemoActivity">


    <com.ouou.demo.myview.view1.TestLinearLayout
        android:id="@+id/viewtest"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_centerInParent="true">
        <com.ouou.demo.myview.view1.TestButton
            android:id="@+id/btn_test"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试按钮"/>

    </com.ouou.demo.myview.view1.TestLinearLayout>

</RelativeLayout>

不复写事件传递里的 任何方法,流程如下:
08-20 11:53:04.094 8523-8523/com.ouou.demo.myview I/gyh﹕ ViewActivity2-dispatchTouchEvent-ACTION_DOWN…
08-20 11:53:04.094 8523-8523/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-dispatchTouchEvent-ACTION_DOWN…
08-20 11:53:04.094 8523-8523/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-onInterceptTouchEvent-ACTION_DOWN…
08-20 11:53:04.094 8523-8523/com.ouou.demo.myview I/gyh﹕ TestButton-dispatchTouchEvent-ACTION_DOWN…
08-20 11:53:04.094 8523-8523/com.ouou.demo.myview I/gyh﹕ testBtn-onTouch-ACTION_DOWN…
08-20 11:53:04.095 8523-8523/com.ouou.demo.myview I/gyh﹕ TestButton-onTouchEvent-ACTION_DOWN…
08-20 11:53:04.209 8523-8523/com.ouou.demo.myview I/gyh﹕ ViewActivity2-dispatchTouchEvent-ACTION_UP…
08-20 11:53:04.210 8523-8523/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-dispatchTouchEvent-ACTION_UP…
08-20 11:53:04.210 8523-8523/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-onInterceptTouchEvent-ACTION_UP…
08-20 11:53:04.210 8523-8523/com.ouou.demo.myview I/gyh﹕ TestButton-dispatchTouchEvent-ACTION_UP…
08-20 11:53:04.210 8523-8523/com.ouou.demo.myview I/gyh﹕ testBtn-onTouch-ACTION_UP…
08-20 11:53:04.210 8523-8523/com.ouou.demo.myview I/gyh﹕ TestButton-onTouchEvent-ACTION_UP…
08-20 11:53:04.211 8523-8523/com.ouou.demo.myview I/gyh﹕ testBtn—onClick…

由Activity的dispatchTouchEvent----Linearlayout的dispatchTouchEvent------------问问它的谋士要不要让孩子知道onInterceptTouchEvent---------孩子的dispatchTouchEvent-----孩子的onTouch监听------孩子的onTouchEvent----孩子的onClick监听。为了更清晰这个流程,下面作如下改动:
1、如果事件传给了孩子们,但孩子没有onTouch和onClick监听怎么办?即将button的onclick和onTouch都注释掉:
打印结果
08-20 11:55:21.330 9298-9298/com.ouou.demo.myview I/gyh﹕ ViewActivity2-dispatchTouchEvent-ACTION_DOWN…
08-20 11:55:21.333 9298-9298/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-dispatchTouchEvent-ACTION_DOWN…
08-20 11:55:21.333 9298-9298/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-onInterceptTouchEvent-ACTION_DOWN…
08-20 11:55:21.333 9298-9298/com.ouou.demo.myview I/gyh﹕ TestButton-dispatchTouchEvent-ACTION_DOWN…
08-20 11:55:21.334 9298-9298/com.ouou.demo.myview I/gyh﹕ TestButton-onTouchEvent-ACTION_DOWN…
08-20 11:55:21.458 9298-9298/com.ouou.demo.myview I/gyh﹕ ViewActivity2-dispatchTouchEvent-ACTION_UP…
08-20 11:55:21.458 9298-9298/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-dispatchTouchEvent-ACTION_UP…
08-20 11:55:21.458 9298-9298/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-onInterceptTouchEvent-ACTION_UP…
08-20 11:55:21.458 9298-9298/com.ouou.demo.myview I/gyh﹕ TestButton-dispatchTouchEvent-ACTION_UP…
08-20 11:55:21.458 9298-9298/com.ouou.demo.myview I/gyh﹕ TestButton-onTouchEvent-ACTION_UP…
因为事件给了孩子们,它没监听也关系不到父亲了,父亲的onClick和onTouch都没执行。
2,如果将TestLinearlayout的onInterceptTouchEvent 改成return true,即不让孩子们知道。
打印结果:
08-20 11:57:15.071 9962-9962/com.ouou.demo.myview I/gyh﹕ ViewActivity2-dispatchTouchEvent-ACTION_DOWN…
08-20 11:57:15.075 9962-9962/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-dispatchTouchEvent-ACTION_DOWN…
08-20 11:57:15.080 9962-9962/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-onInterceptTouchEvent-ACTION_DOWN…
08-20 11:57:15.080 9962-9962/com.ouou.demo.myview I/gyh﹕ testLinelayout-onTouch-ACTION_DOWN…
08-20 11:57:15.080 9962-9962/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-onTouchEvent-ACTION_DOWN…
08-20 11:57:15.176 9962-9962/com.ouou.demo.myview I/gyh﹕ ViewActivity2-dispatchTouchEvent-ACTION_UP…
08-20 11:57:15.177 9962-9962/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-dispatchTouchEvent-ACTION_UP…
08-20 11:57:15.178 9962-9962/com.ouou.demo.myview I/gyh﹕ testLinelayout-onTouch-ACTION_UP…
08-20 11:57:15.178 9962-9962/com.ouou.demo.myview I/gyh﹕ TestLinearLayout-onTouchEvent-ACTION_UP…
08-20 11:57:15.181 9962-9962/com.ouou.demo.myview I/gyh﹕ 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压根就不调用了,事件就此打住。
貌似真相水落石出了,但究竟清楚了没有请看下篇根据自定义复合控件的监听问题再探讨下。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值