Android 事件分發

1. MotionEvent事件

MotionEvent操作裏有多種手勢,常用手勢有

  • ACTION_DOWN,按下
  • ACTION_UP,擡起
  • ACTION_MOVE,移動
  • ACTION_CANCEL,取消

所有的操作都會在ActivityViewViewGroup中處理。
ViewActivity中,有兩個方法dispatchTouchEvent(MotionEvent)onTouchEvent(MotionEvent),在ViewGroup中還有另外一個方法onInterceptTouchEvent(MotionEvent)

2. 測試控件

TouchEventViewTouchEventViewGroup用來跟蹤手勢的傳遞。

public class TouchEventView extends View {
    public final static String LOG_TAG = "TouchEventView";

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

    public TouchEventView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        LogTool.logi(LOG_TAG, "before dispatchTouchEvent " + event.getAction());
        boolean handle = super.dispatchTouchEvent(event);
        LogTool.logi(LOG_TAG, "after dispatchTouchEvent");
        return handle;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        LogTool.logi(LOG_TAG, "before onTouchEvent " + event.getAction());
        boolean handle = super.onTouchEvent(event);
        LogTool.logi(LOG_TAG, "after onTouchEvent");
        return handle;
    }

}

public class TouchEventViewGroup extends RelativeLayout {
    private final static String LOG_TAG = "TouchRelativeLayout";

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        LogTool.logi(LOG_TAG, "before dispatchTouchEvent " + event.getAction());
        boolean handle = super.dispatchTouchEvent(event);
        LogTool.logi(LOG_TAG, "after dispatchTouchEvent");
        return handle;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        LogTool.logi(LOG_TAG, "before onInterceptTouchEvent " + ev.getAction());
        boolean handle = super.onInterceptTouchEvent(ev);
        LogTool.logi(LOG_TAG, "after onInterceptTouchEvent");
        return handle;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        LogTool.logi(LOG_TAG, "before onTouchEvent " + event.getAction());
        boolean handle = super.onTouchEvent(event);
        LogTool.logi(LOG_TAG, "after onTouchEvent");
        return handle;
    }

}

佈局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffffffff" >

    <com.blog.demo.custom.widget.TouchEventViewGroup
        android:layout_width="240dp"
        android:layout_height="300dp"
        android:layout_centerInParent="true"
        android:background="#ffff0000" >

        <com.blog.demo.custom.widget.TouchEventView
            android:layout_width="120dp"
            android:layout_height="150dp"
            android:layout_centerInParent="true"
            android:background="#ff0000ff" />

    </com.blog.demo.custom.widget.TouchEventViewGroup>

</RelativeLayout>

3. MotionEvent流程

Activity裏面有一個ViewGroupViewGroup裏面包含一個View,點擊View後會發出MotionEvent事件,每個事件都會從ACTION_DOWN開始,到ACTION_UP結束,也會有ACTION_MOVEACTION_CANCEL

  • 每次ACTION_DOWN,都會調用dispatchTouchEvent(MotionEvent)方法,從Activity->ViewGroup->View,一層層往下傳遞。在ViewdispatchTouchEvent(MotionEvent)中調用onTouchEvent(MotionEvent),返回false的話,往上傳遞給ViewGroupActivity,依次調用onTouchEvent(MotionEvent)

  • 如果ACTION_DOWN沒有在ViewViewGroup中沒有處理,Activity將在onTouchEvent(MotionEvent)中處理接下來的所有事件。如果ACTION_DOWNViewViewGroup處理,但沒有處理其他MotionEvent事件,Activity依然會調用onTouchEvent(MotionEvent)

  • 如果在ViewdispatchTouchEvent(MotionEvent)或者onTouchEvent(MotionEvent)裏面返回true,表明View將處理Action,不會調用ViewGroupActivyonTouchEvent(MotionEvent)方法。其它MotionEvent事件先在ViewonTouchEvent(MotionEvent)中處理。未處理的交給ActivityonTouchEvent(MotionEvent)方法。

  • 如果在ViewGroupdispatchTouchEvent(MotionEvent)或者onTouchEvent(MotionEvent)裏面返回true,表明ViewGroup將處理。接下來的MotionEvent事件先在ViewGroup中的onTouchEvent(MotionEvent)中處理。未處理的交給ActivityonTouchEvent(MotionEvent)方法。

  • ViewGroup中會先調用onInterceptTouchEvent(MotionEvent)過濾,然後纔會傳遞給View。如果ViewGrouponInterceptTouchEvent(MotionEvent)返回true,直接調用ViewGrouponTouchEvent(MotionEvent)onInterceptTouchEvent(MotionEvent)只會調用一次。

  • 如果View中處理了ACTION_DOWN,在其它MotionEvent事件時ViewGrouponInterceptTouchEvent(MotionEvent)返回true,則會傳遞ACTION_CANCELView,接下來的MotionEvent事件都會調用ViewGrouponTouchEvent(MotionEvent)處理。

4. 總結

  • 沒有處理的事件都會在ActivityonTouchEvent(MotionEvent)中處理。

  • ACTION_DOWN事件在哪裏處理,接下來的MotionEvent事件都只會傳遞到那裏。

  • 如果ViewGroup中的onInterceptTouchEvent(MotionEvent)返回true,事件將不會再往下傳遞。onInterceptTouchEvent(MotionEvent)返回true後不會再次調用。

  • 如果ViewGroup的子控件在ACTION_DOWN時處理了事件,那麼在onInterceptTouchEvent(MotionEvent)返回true後代替子控件處理MotionEvent

6. OnTouchListener

View可以添加監聽器,使用setOnTouchListener(OnTouchListener)方法添加。在dispatchTouchEvent(MotionEvent)中,先調用OnTouchListeneronTouch(View, MotionEvent)。如果返回true,返回到上一層,否則調用onTouchEvent(MotionEvent)

public boolean dispatchTouchEvent(MotionEvent event) {
    ... ...

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

    ... ...
}

相關文章
Android 事件分發
Android Scroller和VelocityTracker類

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章