`

MotionEvent事件在onInterceptTouchEvent()、onTouchEvent()中的传递顺序

阅读更多

重要概念,ACTION_DOWN是事件的始祖,没有它,up和move都不会被传递。当发生了一个down事件时,系统会根据自己的判断来生成第二个事件,比如up和move。然后再传递给相应的响应函数。

onInterceptTouchEvent应用的时机:handler touch event in parent before the event dispatch to the child

如何使用: create a class extents from ViewGroup(like LinearLayout ...) class and override the onInterceptTouchEvent(), the you can process something you want in the method.

 

转自:http://www.cnblogs.com/rocky_yi/archive/2011/01/21/1941522.html#

onInterceptTouchEvent()用于处理事件并改变事件的传递方向。处理事件这个不用说了,你在函数内部编写代码处理就可以了。而决定传递方向的是返回值,返回为false时事件会传递给子控件的onInterceptTouchEvent();返回值为true时事件会传递给当前控件的onTouchEvent(),而不在传递给子控件,这就是所谓的Intercept(截断)。

onTouchEvent() 用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。可能你要问是否消费了又区别吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。

<?xml version="1.0" encoding="utf-8"?>
<com.touchstudy.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation
="vertical"
    android:layout_width
="fill_parent"
    android:layout_height
="fill_parent" >
    
<com.touchstudy.LayoutView2
        
android:orientation="vertical"
        android:layout_width
="fill_parent"
        android:layout_height
="fill_parent"
        android:gravity
="center">
       
<com.touchstudy.MyTextView 
            
android:layout_width="wrap_content"
            android:layout_height
="wrap_content"
            android:id
="@+id/tv"
            android:text
="AB"
            android:textSize
="40sp"
            android:textStyle
="bold"
            android:background
="#FFFFFF"
            android:textColor
="#0000FF"/>
   
</com.touchstudy.LayoutView2>
</com.touchstudy.LayoutView1>

 

 

在没有重写onInterceptTouchEvent()和onTouchEvent()的情况下(他们的返回值都是false), 对上面这个布局,MotionEvent事件的传递顺序如下:

 

当某个控件的onInterceptTouchEvent()返回值为true时,就会发生截断,事件被传到当前控件的onTouchEvent()。如我们将LayoutView2的onInterceptTouchEvent()返回值为true,则传递流程变成:

 

 如果我们同时将LayoutView2的onInterceptTouchEvent()和onTouchEvent()设置成true,那么LayoutView2将消费被传递的事件,同时后续事件(如跟着ACTION_DOWN的ACTION_MOVE或者ACTION_UP)会直接传给LayoutView2的onTouchEvent(),不传给其他任何控件的任何函数。同时传递给子空间一个ACTION_CANCEL事件。传递流程变成(图中没有画出ACTION_CANCEL事件):

          

附SDK给出的说明:

·  You will receive the down event here.
·  The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
·  For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
·  If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.


ontouch函数和onclick等响应函数的关系:

针对屏幕上的一个View控件,Android如何区分应当触发onTouchEvent,还是onClick,亦或是onLongClick事件?

 

Android中,一次用户操作可以被不同的View按次序分别处理,并将完全响应了用户一次UI操作称之为消费了该事件(consume),那么Android是按什么次序将事件传递的呢?又在什么情况下判定为消费了该事件?

      搞清楚这些问题对于编写出能正确响应UI操作的代码是很重要的,尤其当屏幕上的不同View需要针对此次UI操作做出各种不同响应的时候更是如此,一个典型例子就是用户在桌面上放置了一个Widget,那么当用户针对widget做各种操作时,桌面本身有的时候要对用户的操作做出响应,有时忽略。只有搞清楚事件触发和传递的机制才有可能保证在界面布局非常复杂的情况下,UI控件仍然能正确响应用户操作。

 

1.  onTouchEvent

     onTouchEvent中要处理的最常用的3个事件就是:ACTION_DOWN、ACTION_MOVE、ACTION_UP。

     这三个事件标识出了最基本的用户触摸屏幕的操作,含义也很清楚。虽然大家天天都在用它们,但是有一点请留意,ACTION_DOWN事件作为起始事件,它的重要性是要超过ACTION_MOVEACTION_UP的,如果发生了ACTION_MOVE或者ACTION_UP,那么一定曾经发生了ACTION_DOWN

     从Android的源代码中能看到基于这种不同重要性的理解而实现的一些交互机制,SDK中也有明确的提及,例如在ViewGrouponInterceptTouchEvent方法中,如果在ACTION_DOWN事件中返回了true,那么后续的事件将直接发给onTouchEvent,而不是继续发给onInterceptTouchEvent

 

2.  onClickonLongClickonTouchEvent

     曾经看过一篇帖子提到,如果在View中处理了onTouchEvent,那么就不用再处理onClick了,因为Android只会触发其中一个方法。这个理解是不太正确的,针对某个view,用户完成了一次触碰操作,显然从传感器上得到的信号是手指按下和抬起两个操作,我们可以理解为一次Click,也可以理解为发生了一次ACTION_DOWN和ACTION_UP,那么Android是如何理解和处理的呢?

     在Android中,onClickonLongClick的触发是和ACTION_DOWNACTION_UP相关的,在时序上,如果我们在一个View中同时覆写了onClickonLongClickonTouchEvent的话,onTouchEvent是最先捕捉到ACTION_DOWNACTION_UP事件的,其次才可能触发onClick或者onLongClick。主要的逻辑在View.java中的onTouchEvent方法中实现的:(onTouchEvent根据情况来分发给onLongClick或onClick,一旦其中某个函数返回了true,说明此次ui事件已经结束,就不再向自己,或别的视图分发该消息)

 

case MotionEvent.ACTION_DOWN:

    mPrivateFlags |= PRESSED;

    refreshDrawableState();

    if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {

         postCheckForLongClick(); 

    }

    break;

case MotionEvent.ACTION_UP:

    if ((mPrivateFlags & PRESSED) != 0) {

         boolean focusTaken = false;

         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {

               focusTaken = requestFocus();

         }

 

    if (!mHasPerformedLongPress) {

       if (mPendingCheckForLongPress != null) {

             removeCallbacks(mPendingCheckForLongPress);

       }

       if (!focusTaken) {

              performClick();

       }

    }

    …

    break;


     可以看到,Click的触发是在系统捕捉到ACTION_UP后发生并由performClick()执行的,performClick里会调用先前注册的监听器的onClick()方法:

public boolean performClick() {

    …

    if (mOnClickListener != null) {

        playSoundEffect(SoundEffectConstants.CLICK);

        mOnClickListener.onClick(this);

        return true;

    }

        return false;

}

 

LongClick的触发则是从ACTION_DOWN开始,由postCheckForLongClick()方法完成:

private void postCheckForLongClick() {

     mHasPerformedLongPress = false;

     if (mPendingCheckForLongPress == null) {

         mPendingCheckForLongPress = new CheckForLongPress();

     }

     mPendingCheckForLongPress.rememberWindowAttachCount();

     postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());

}

可以看到,在ACTION_DOWN事件被捕捉后,系统会开始触发一个postDelayed操作,delay的时间在Eclair2.1上为500ms500ms后会触发CheckForLongPress线程的执行:

class CheckForLongPress implements Runnable {

        public void run() {

            if (isPressed() && (mParent != null)

                    && mOriginalWindowAttachCount == mWindowAttachCount) {

                if (performLongClick()) {

                    mHasPerformedLongPress = true;

                }

            }

        }

}


如果各种条件都满足,那么在CheckForLongPress中执行performLongClick(),在这个方法中将调用onLongClick()

public boolean performLongClick() {

      …

      if (mOnLongClickListener != null) {

          handled = mOnLongClickListener.onLongClick(View.this);

      }

      …

}


从实现中可以看到onClick()onLongClick()方法是由ACTION_DOWNACTION_UP事件捕捉后根据各种情况最终确定是否触发的,也就是说如果我们在一个Activity或者View中同时监听或者覆写了onClick(),onLongClick()onTouchEvent()方法,并不意味着只会发生其中一种。

 

下面是一个onClick被触发的基本时序的Log

04-05 05:57:47.123: DEBUG/TSActivity(209): onTouch ACTION_DOWN

04-05 05:57:47.263: DEBUG/TSActivity(209): onTouch ACTION_UP

04-05 05:57:47.323: DEBUG/TSActivity(209): onClick

可以看出是按ACTION_DOWN -> ACTION_UP -> onClick的次序发生的。

 

下面是一个onLongClick被触发的基本时序的Log

04-05 06:00:04.133: DEBUG/TSActivity(248): onTouch ACTION_DOWN

04-05 06:00:04.642: DEBUG/TSActivity(248): onLongClick 

04-05 06:00:05.083: DEBUG/TSActivity(248): onTouch ACTION_UP

可以看到,在保持按下的状态一定时间后会触发onLongClick,之后抬起手才会发生ACTION_UP

 

3.  onClickonLongClick能同时发生吗?

     要弄清楚这个问题只要理解Android对事件处理的所谓消费(consume)概念即可,一个用户的操作会被传递到不同的View控件和同一个控件的不同监听方法处理,任何一个接收并处理了该次事件的方法如果在处理完后返回了true,那么该次event就算被完全处理了,其他的View或者监听方法就不会再有机会处理该event了。

     onLongClick的发生是由单独的线程完成的,并且在ACTION_UP之前,而onClick的发生是在ACTION_UP后,因此同一次用户touch操作就有可能既发生onLongClick又发生onClick。这样是不是不可思议?所以及时向系统表示“我已经完全处理(消费)了用户的此次操作”,是很重要的事情。例如,我们如果在onLongClick()方法的最后return true,那么onClick事件就没有机会被触发了。

 

下面的Log是在onLongClick()方法return false的情况下,一次触碰操作的基本时序:

04-05 06:00:53.023: DEBUG/TSActivity(277): onTouch ACTION_DOWN

04-05 06:00:53.533: DEBUG/TSActivity(277): onLongClick 

04-05 06:00:55.603: DEBUG/TSActivity(277): onTouch ACTION_UP

04-05 06:00:55.663: DEBUG/TSActivity(277): onClick

可以看到,在ACTION_UP后仍然触发了onClick()方法。

 

分享到:
评论

相关推荐

    onTouchEvent 、onInterceptTouchEvent的顺序~

    在博客《onTouchEvent 、onInterceptTouchEvent的顺序》中,作者可能详细解释了这两个方法在实际应用中的操作流程: 1. **默认行为**: 当用户触摸屏幕时,事件首先由父视图的 `dispatchTouchEvent` 处理,如果父...

    onInterceptTouchEvent和onTouchEvent使用demo

    如果在某个父View中`onInterceptTouchEvent`返回`true`,则表示该父View想要拦截并处理这个事件,后续的触摸事件将不再传递给子View。默认情况下,`onInterceptTouchEvent`返回`false`,表示不拦截事件,事件将继续...

    onInterceptTouchEvent和onTouchEvent调用关系详解

    这个事件会沿着View树自上而下进行分发,首先是父View,然后是子View,直到某个View消费掉事件或者事件传递回父View。 `onInterceptTouchEvent`是父View( ViewGroup)决定是否拦截子View事件的入口。当一个...

    View的onInterceptTouchEvent(MotionEvent ev)和dispatchTouchEvent(MotionEvent ev) 解析

    `dispatchTouchEvent` 负责将事件传递给合适的处理者,通常是调用 `onTouchEvent(MotionEvent ev)` 方法。对于ViewGroup,它会根据 `onInterceptTouchEvent` 的结果来决定是否将事件传递给子View,或者自己处理。 ...

    浅谈Android onTouchEvent 与 onInterceptTouchEvent的区别详解

    例如,我们可以拦截触摸事件来实现滑动切换页面的效果,或者在`ViewGroup`中对特定的触摸事件进行特殊处理,而让其他事件传递给子`View`。 以文中给出的例子为例,创建了`LLayout`(继承自`FrameLayout`)和`LView`...

    android触摸消息传递

    在你的项目中,`AndroidTest_onInterceptTouchEvent`很可能是一个示例应用,用于演示如何利用`MotionEvent`和`onTouchEvent`,以及`onInterceptTouchEvent`来实现触摸事件的处理和传递。通过调试和分析这个示例,你...

    View的onInterceptTouchEvent(MotionEvent ev)和dispatchTouchEvent(MotionEvent ev)

    `onInterceptTouchEvent(MotionEvent ev)` 和 `dispatchTouchEvent(MotionEvent ev)` 是View和ViewGroup在处理触摸事件时的两个关键方法,它们共同构成了触摸事件的分发机制。了解这两个方法的工作原理对于优化用户...

    onInterceptTouchEvent

    2. **事件传递**:如果ViewGroup不拦截(即`onInterceptTouchEvent()`返回false),事件会沿着布局树向下传递到直接子视图。如果子视图消费了事件(`onTouchEvent()`返回true),事件流停止;否则,事件会回溯到父...

    Android事件处理机制ViewGroup的事件传递

    在本主题中,我们将深入探讨`ViewGroup`的事件传递,即事件分发过程。 1. **事件模型** Android事件处理基于一个简单的模型:当用户与屏幕上的UI元素交互时,系统会产生事件(如点击、滑动等)。这些事件首先被最...

    Android事件传递机制

    在Android系统中,事件传递机制是用户界面交互的核心部分,主要处理用户与屏幕的各种触摸操作。这个机制确保了用户点击屏幕时,相应的视图能够正确地接收到并响应这些事件。我们今天将深入探讨Android的触摸事件...

    Android TouchEvent事件传递

    1. 视图滑动:在实现滑动效果时,通常需要在ViewGroup的onInterceptTouchEvent()中判断滑动方向,当满足滑动条件时拦截事件,否则让事件传递给子View。 2. 多点触控:Android支持同时处理多个MotionEvent,通过...

    android Touch事件传递机制

    在Android系统中,触摸事件(Touch Events)是用户与设备交互的主要方式,它涉及到了复杂的事件传递机制。本文将深入探讨Android的触摸事件处理流程,包括事件的产生、分发以及处理过程,以实例的形式帮助开发者理解...

    事件传递机制Demo

    在Android开发中,事件传递机制是用户界面交互的核心部分,它决定了用户在屏幕上触摸或点击时,事件如何在各个View之间进行分发。本Demo,"事件传递机制Demo",着重展示了这一机制的工作原理和常见应用场景。我们...

    android事件传递机制

    在Android系统中,事件传递机制是用户界面交互的基础,它涉及到Activity、View、 ViewGroup等组件之间的协同工作。本文将深入探讨这一机制,帮助你理解如何在Android应用中处理触摸事件。 1. 触摸事件的起源 触摸...

    android 事件传递demo

    在Android开发中,事件传递是应用界面交互的关键部分,尤其是触摸事件(Touch Event)的处理。本示例“android事件传递demo”旨在演示如何在Android应用程序中管理触摸事件的分发流程,帮助开发者理解并有效地处理用户...

    Android中Touch事件的传递

    ViewGroup的onInterceptTouchEvent(MotionEvent ev)方法可以拦截事件传递,这个方法默认返回false,表示不拦截。如果重写这个方法并返回true,那么Touch事件就不会被传递给ViewGroup的子控件,而是转而由ViewGroup...

    Android事件模型之interceptTouchEvnet ,onTouchEvent关系正解

    为了调试和优化触摸事件处理,开发者可以使用Logcat输出`MotionEvent`的信息,或者使用Android Studio的Layout Inspector查看视图树中的事件传递路径。此外,理解Android的触摸事件标志(如ACTION_DOWN、ACTION_UP、...

    android Touch触发事件的生命周期

    2. **Intercept阶段**:在事件传递过程中,ViewGroup可以通过重写`onInterceptTouchEvent()`方法来拦截事件。如果`onInterceptTouchEvent()`返回true,那么后续的事件将不再传递给子View,而是由当前ViewGroup处理。...

    安卓onTouchEvent上下左右滑动事件

    为了实现上下左右滑动,我们需要在`onTouchEvent`中捕获ACTION_DOWN和ACTION_MOVE事件,并记录初始触点位置和后续移动的位置。以下是关键步骤: 1. **初始化状态**:在ACTION_DOWN事件中,获取当前触摸点的坐标(x1...

    android中dispatchTouchEvent和onInterceptTouchEvent研究

    但如果LinearLayout有需要响应触摸的子View,那么就需要在ScrollView的`onInterceptTouchEvent`中判断,只有当ScrollView没有滑动需求时,才允许事件传递给LinearLayout。 了解这两个方法的工作机制后,开发者可以...

Global site tag (gtag.js) - Google Analytics