Android事件分發(fā)機制(下) View的事件處理
綜述
在上篇文章Android中的事件分發(fā)機制(上)——ViewGroup的事件分發(fā)中,對ViewGroup的事件分發(fā)進(jìn)行了詳細(xì)的分析。在文章的最后ViewGroup的dispatchTouchEvent方法調(diào)用dispatchTransformedTouchEvent方法成功將事件傳遞給ViewGroup的子View。并交由子View進(jìn)行處理。那么現(xiàn)在就來分析一下子View接收到事件以后是如何處理的。
View的事件處理
對于這里描述的View,它是ViewGroup的父類,并不包含任何的子元素。這也就意味著View無法再次向下對事件進(jìn)行分發(fā)操作,因此在View中并不存在onInterceptTouchEvent方法,也不會對事件做出攔截操作。它所做的事情就是對所接收的事件進(jìn)行處理。下面就開看一下View如何對事件進(jìn)行處理的。
既然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方法中對事件處理的核心部分體現(xiàn)在上述代碼中。onFilterTouchEventForSecurity方法表示當(dāng)前接收事件的view是否處于被遮蓋狀態(tài),View處于被遮蓋狀態(tài)表示當(dāng)前View不位于頂部,該view被其它View所覆蓋。如果當(dāng)前View被遮蓋,那么該View不會對事件進(jìn)行處理。
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; }
在結(jié)合上述一段代碼可以看到,通過setOnTouchListener方法設(shè)置OnTouchListener以后,若是當(dāng)前View處于可用狀態(tài),那么條件li != null && li.mOnTouchListener !=null && (mViewFlags & ENABLED_MASK) == ENABLED必然為true。這時候程序便會回調(diào)OnTouchListener中的onTouch方法,若是在onTouch方法中返回true,便不會在執(zhí)行View的onTouchEvent方法。從這里我們能夠看到,一旦設(shè)置了OnTouchListener,那么OnTouchListener的優(yōu)先級要高于onTouchEvent。
有一點需要注意,在程序中設(shè)置了OnTouchListener以后,對于OnTouchListener中的onTouch的返回值并不代表View中的dispatchTouchEvent方法所返回的值。在onTouch方法返回true的時候,表示事件成功被當(dāng)前View所消耗,這時候result被置為true并且不再執(zhí)行onTouchEvent,所以dispatchTouchEvent也就返回true??墒且坏┰趏nTouch方法中返回false。這時候便會調(diào)用onTouchEvent方法,如果事件被onTouchEvent成功處理,并返回true,result依然會被置為true,dispatchTouchEvent自然而然的也就返回true。
下面在進(jìn)入View的onTouchEvent方法一探究竟。對于onTouchEvent方法里的內(nèi)容比較多,在這里分段查看。
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,如果他們的一些點擊事件可用的話,依然能夠成功的消費事件,只是它不會為該事件做出響應(yīng)。對于View的這些點擊之間默認(rèn)為不可用,但是對于不同的的View他們的默認(rèn)值不一樣。例如在ImageView中點擊事件依然為不可用,但是在Button中點擊事件為可用。當(dāng)然如果手動為它們設(shè)置監(jiān)聽事件,那么這些監(jiān)聽事件都將會自動被設(shè)為可用狀態(tài)。從如下源碼中可以看出。
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; } }
這里首先判斷是否對事件設(shè)置了代理,如果對事件設(shè)置了代理,便會執(zhí)行TouchDelegate的onTouchEvent方法。mTouchDelegate默認(rèn)值為null,可以通過View的setTouchDelegate方法來設(shè)置代理。對于TouchDelegate在后續(xù)的文章中在進(jìn)行詳細(xì)分析,在這里就不在過多描述。
最后看一下View是如何處理事件的,對于接收的事件整個處理過程比較復(fù)雜,在這里就從宏觀上來整體看一下它的處理機制。
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的點擊事件處于可用狀態(tài)的話,便會對于這些事件進(jìn)行處理,并且返回true。當(dāng)一個事件序列完成以后調(diào)用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; }
從上面代碼中可以看出,如果我們設(shè)置了OnClickListener,便會調(diào)用它的onClick方法。從這一路下來我們可以看出對于View的onClick事件,在最后才會被調(diào)用,可見onClick的優(yōu)先級是最低的。
總結(jié)
在這里對View的事件處理做一下總結(jié)。在ViewGroup將事件分發(fā)到View以后。在View里面通過OnTouchListener的onTouch和View中的onTouchEvent這兩個方法對事件進(jìn)行處理。對于onTouch方法是View提供給用戶的,方便用戶自己處理觸摸事件,而onTouchEvent是Android系統(tǒng)自己實現(xiàn)的接口。若是用戶設(shè)置了OnTouchListener,Android系統(tǒng)會首先調(diào)用OnTouchListener的onTouch方法。若是在onTouch方法中返回true,就不在執(zhí)行View的onTouchEvent方法。只有在onTouch中返回了false才會執(zhí)行onTouchEvent。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android簡單實現(xiàn)一個顏色漸變的ProgressBar的方法
本篇文章主要介紹了Android簡單實現(xiàn)一個顏色漸變的ProgressBar的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12Android socket如何實現(xiàn)文件列表動態(tài)訪問
本文介紹Android socket實現(xiàn)文件列表動態(tài)訪問,訪問文件夾之后通過listview展示,并在點擊文件夾后進(jìn)入文件夾,獲得其內(nèi)容,有此需求的朋友可以參考下2021-06-06Flutter使用texture_rgba_renderer實現(xiàn)桌面端渲染視頻詳解
這篇文章主要為大家介紹了Flutter如何使用texture_rgba_renderer實現(xiàn)桌面端渲染視頻,文中的示例代碼講解詳細(xì),需要的可以了解一下2023-07-07用Eclipse搭建Android開發(fā)環(huán)境并創(chuàng)建第一個Android項目(eclipse+android sdk)
這篇文章主要介紹了用Eclipse搭建Android開發(fā)環(huán)境并創(chuàng)建第一個Android項目,需要的朋友可以參考下2015-09-09Android_RecyclerView實現(xiàn)上下滾動廣告條實例(帶圖片)
本篇文章主要介紹了Android_RecyclerView實現(xiàn)上下滾動廣告條實例(帶圖片),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06