Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中的事件分發機制(下)——View的事件處理

Android中的事件分發機制(下)——View的事件處理

編輯:關於Android編程

綜述

上篇文章,對ViewGroup的事件分發進行了詳細的分析。在文章的最後ViewGroup的dispatchTouchEvent方法調用dispatchTransformedTouchEvent方法成功將事件傳遞給ViewGroup的子View。並交由子View進行處理。那麼現在就來分析一下子View接收到事件以後是如何處理的。

View的事件處理

對於這裡描述的View,它是ViewGroup的父類,並不包含任何的子元素。這也就意味著View無法再次向下對事件進行分發操作,因此在View中並不存在onInterceptTouchEvent方法,也不會對事件做出攔截操作。它所做的事情就是對所接收的事件進行處理。下面就開看一下View如何對事件進行處理的。
既然ViewGroup將事件交由View的dispatchTouchEvent方。那麼首先在這裡就來看一下dispatchTouchEvent裡面做了什麼事情。

public boolean dispatchTouchEvent(MotionEvent event) {

    ......

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

    ......

    return result;
}

  在View的dispatchTouchEvent方法中對事件處理的核心部分體現在上述代碼中。onFilterTouchEventForSecurity方法表示當前接收事件的view是否處於被遮蓋狀態,View處於被遮蓋狀態表示當前View不位於頂部,該view被其它View所覆蓋。如果當前View被遮蓋,那麼該View不會對事件進行處理。

public interface OnTouchListener {
    boolean onTouch(View v, MotionEvent event);
}

public void setOnTouchListener(OnTouchListener l) {
    getListenerInfo().mOnTouchListener = l;
}

ListenerInfo getListenerInfo() {
    if (mListenerInfo != null) {
        return mListenerInfo;
    }
    mListenerInfo = new ListenerInfo();
    return mListenerInfo;
}

在結合上述一段代碼可以看到,通過setOnTouchListener方法設置OnTouchListener以後,若是當前View處於可用狀態,那麼條件li != null && li.mOnTouchListener !=null && (mViewFlags & ENABLED_MASK) == ENABLED必然為true。這時候程序便會回調OnTouchListener中的onTouch方法,若是在onTouch方法中返回true,便不會在執行View的onTouchEvent方法。從這裡我們能夠看到,一旦設置了OnTouchListener,那麼OnTouchListener的優先級要高於onTouchEvent。
有一點需要注意,在程序中設置了OnTouchListener以後,對於OnTouchListener中的onTouch的返回值並不代表View中的dispatchTouchEvent方法所返回的值。在onTouch方法返回true的時候,表示事件成功被當前View所消耗,這時候result被置為true並且不再執行onTouchEvent,所以dispatchTouchEvent也就返回true。可是一旦在onTouch方法中返回false。這時候便會調用onTouchEvent方法,如果事件被onTouchEvent成功處理,並返回true,result依然會被置為true,dispatchTouchEvent自然而然的也就返回true。
下面在進入View的onTouchEvent方法一探究竟。對於onTouchEvent方法裡的內容比較多,在這裡分段查看。

if ((viewFlags & ENABLED_MASK) == DISABLED) {
    if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
        setPressed(false);
    }
    // A disabled view that is clickable still consumes the touch
    // events, it just doesn't respond to them.
    return (((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}

在這裡可以看出對於不可用的View,如果他們的一些點擊事件可用的話,依然能夠成功的消費事件,只是它不會為該事件做出響應。對於View的這些點擊之間默認為不可用,但是對於不同的的View他們的默認值不一樣。例如在ImageView中點擊事件依然為不可用,但是在Button中點擊事件為可用。當然如果手動為它們設置監聽事件,那麼這些監聽事件都將會自動被設為可用狀態。從如下源碼中可以看出。

public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}

public void setOnLongClickListener(@Nullable OnLongClickListener l) {
    if (!isLongClickable()) {
        setLongClickable(true);
    }
    getListenerInfo().mOnLongClickListener = l;
}

public void setOnContextClickListener(@Nullable OnContextClickListener l) {
    if (!isContextClickable()) {
        setContextClickable(true);
    }
    getListenerInfo().mOnContextClickListener = l;
}

下面接著看OnTouchEvent裡面代碼。

if (mTouchDelegate != null) {
    if (mTouchDelegate.onTouchEvent(event)) {
        return true;
    }
}

這裡首先判斷是否對事件設置了代理,如果對事件設置了代理,便會執行TouchDelegate的onTouchEvent方法。mTouchDelegate默認值為null,可以通過View的setTouchDelegate方法來設置代理。對於TouchDelegate在後續的文章中在進行詳細分析,在這裡就不在過多描述。
最後看一下View是如何處理事件的,對於接收的事件整個處理過程比較復雜,在這裡就從宏觀上來整體看一下它的處理機制。

if (((viewFlags & CLICKABLE) == CLICKABLE ||
        (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
        (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
    switch (action) {
        case MotionEvent.ACTION_UP:

            ......

                if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                    // 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();
                        }
                    }
                }

            ......

            break;

        ......

    }

    return true;
}

如果View的點擊事件處於可用狀態的話,便會對於這些事件進行處理,並且返回true。當一個事件序列完成以後調用performClick方法,下面看下這個performClick方法。

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

從上面代碼中可以看出,如果我們設置了OnClickListener,便會調用它的onClick方法。從這一路下來我們可以看出對於View的onClick事件,在最後才會被調用,可見onClick的優先級是最低的。

總結

在這裡對View的事件處理做一下總結。在ViewGroup將事件分發到View以後。在View裡面通過OnTouchListener的onTouch和View中的onTouchEvent這兩個方法對事件進行處理。對於onTouch方法是View提供給用戶的,方便用戶自己處理觸摸事件,而onTouchEvent是Android系統自己實現的接口。若是用戶設置了OnTouchListener,Android系統會首先調用OnTouchListener的onTouch方法。若是在onTouch方法中返回true,就不在執行View的onTouchEvent方法。只有在onTouch中返回了false才會執行onTouchEvent。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved