Anroid事件分发

   因为最近因个人原因离职,面试的时候,有人问到了Android中事件分发机制的过程,因为忘得差不多了,没答好,所以回来后,想写了个Demo,重新复习一遍。

 

   一般来说,Android的组件其实可以分为两类,即View和ViewGroup,而ViewGroup又继承于View。对于ViewGroup的是事件分发主要涉及到三个方法:

  dispatchTouchEvent(MotionEvent ev),onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent event),而View主要涉及到

  dispatchTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent event)。顾名思义,dispatchTouchEvent用于分发Touch事件,

  onInterceptTouchEvent用于拦截Touch事件,而onTouchEvent则是用于处理Touc事件。

 

   首先,我们写一个简单的Demo来看看,

public class MyLinearLayout extends LinearLayout {

    public MyLinearLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

    public MyLinearLayout(Context context) {
        super(context);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("test", "MyLinearLayout"+" dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("test", "MyLinearLayout"+" onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("test", "MyLinearLayout"+" onTouchEvent");
        return super.onTouchEvent(event);
    }
    
}
public class MyView extends TextView {

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    public MyView(Context context) {
        super(context);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("test", "MyView"+" dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("test", "MyView"+" onTouchEvent");
        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: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="com.example.eventdemo.MainActivity" >

    <com.example.test1.MyLinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >
        <com.example.test1.MyView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00000000000000000000"
            />
        
    </com.example.test1.MyLinearLayout>

</RelativeLayout>

 

上面我主要定义了一个ViewGroup(MyLinearLayout)和一个View(MyView),然后分别打印出dispatchTouchEvent(MotionEvent ev),onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent event)的Log:

 通过上面的Log可以看出,ViewGroup和View中的事件默认分发顺序是:ViewGruop.dispatchTouchEvent --> ViewGruop.onInterceptTouchEvent

 --> View.dispatchTouchEvent --> View.onTouchEvent --> ViewGroup.onTouchEvent.

 

 那如果我们对View的onTouchEvent事件的返回值修改下,会变成什么样呢?

 首先,我将MyView中的onTouchEvent方法中直接return true,然后看下Log:

 

 可以发现,MyLinearLayout的onTouchEvent没有执行,这是因为这次Touch事件已经被MyView消费了,即如果我们在方法中直接return true,那么事件就不会继续分发下去了。

 

 接着,我们在MyLinearLayout的dispatchTouchEvent方法中return true,然后看Log:

 总结如下:

  • dispatchTouchEvent返回true,意味后续不会将事件进行分发,而是直接在dispatchTouchEvent中进行拦截,即后续的ACTION_MOVE,ACTION_UP,ACTION_CANCEL事件只会触发ViewGroup和ViewGroup之前路径的View以及ViewGroup(需要注意的是,如果是在onTouch中,返回true,则下次的ACTION_DOWN,ACTION_UP就只会触发该ViewGroup的onTouch事件,即系统是会记住消费了onTouch事件的那个ViewGroup来传递事件)的dispatchTouchEvent和onInterceptTouchEvent
    ,而不会触发ViewGroup以及ViewGroup之后的View的onInterceptTouchEvent()和onTouchEvent()事件.
  • dispatchTouchEvent返回返回false,意味着不处理该事件,也不往下分发事件。即后续不会收到ACTION_MOVE,ACTION_UP,ACTION_CANCEL事件.
  • 返回super.dispatchTouchEvent(ev)意味着不处理该事件,而是依次向子View分发该事件,直到找到需要消费本次事件的View.

 

  所以如果我们需要将事件拦截下来的话,直接return true就可以了。

posted on 2015-12-03 22:08  小样来玩玩  阅读(127)  评论(0编辑  收藏  举报

导航