天天看點

從源碼說說dispatchTouchEvent與onTouchEvent的關系以及OnTouchListener的用法

為什麼要說這個問題呢,這段時間還是在SeekBar上遇到一些問題,好像ADT并沒有給seekBar在xml中提供enabled屬性,雖然我們可以在代碼中設定,但是它并不能實作下面這個需求,是以我們需要找到别的方式去實作它,也為了能更好的了解一下dispatchTouchEvent與onTouchEvent之間的關系。

我們現在有這麼一個問題,我們需要使SeekBar在某些時候隻可點選不可拖動,如果簡簡單單使用enabled的話,那麼隻會不能移動,這是不滿足的。是以我們需要有這麼一個入口去逐漸解決這個問題。

入口點:

需要知道seekbar是如何拖動與繪制的。

首先會想到去哪找這個問題?肯定是SeekBar的onTouchEvent方法對不對?我們在拖拽seekBar的thumb的時候,肯定會觸發MotionEvent.ACTION_MOVE的事件,這樣一來,我們就找到了我們問題當然入口處,隻用合理控制onTouchEvent方法的調用就可以了,我們從源碼開始看:

在SeekBar類中是沒有onTouchEvent方法的,那麼我們需要去它的父類中找,如果父類中沒有,那麼就需要去父類的父類中去找,直到找到為止,那麼我們很快的就在SeekBar的父類AbsSeekBar中找到了onTouchEvent方法:

來分析下這段代碼:

mIsUserSeekable在AbsSeekBar中聲明為了true,後面也沒有任何地方對它進行修改。isEnabled由于沒有對它進行專門設定,是以這裡恒為true。是以if中的判斷恒為false。接着往下MotionEvent.ACTION_DOWN中的if(isInScrollingContainer())表示是說如果是在可以滾動的容器當中,我們這裡使用的是平常的布局,是以這裡為false。

setPressed(true)更改按壓狀态,invalidate(mThumb.getBounds())繪制thumb,onStartTrackingTouch設定mIsDragging為true為ACTION_MOVE做準備,trackTouchEvent對内部進行測量計算繪制界面,attemptClaimDrag禁止父布局及祖先布局阻斷touch事件。好,接下來就是MotionEvent.ACTION_MOVE了,已知mIsDragging是true,是以當執行move的時候一直是在調用trackTouchEvent方法了,是以,我們在拖拽的seekBar的thumb的時候就是在這裡進行的處理。好,有些扯遠了,回到我們的主要部分:

我們剛剛可以看到onTouchEvent中沒有任何一處可以對我們的觸摸事件進行攔截,那我們就需要找在哪裡調用了onTouchEvent方法,我們從SeekBar到其父類AbsSeekBar再到ProgressBar,最後到View的dispatchTouchEvent中被調用了:

我們先不管其它,隻關心我們關心的:

我們先不管外面那個方法,隻看 内部:這裡判斷了li.mOnTouchListener是否為空,控件是否可用,最後通過mOnTouchListener.onTouch将事件傳遞進去,這裡的傳回值就很關鍵了,如果傳回false,則代碼會繼續向下執行,最後交給onTouchEvent,如果傳回true,則直接傳回,不會再執行onTouchEvent方法。是以為了解決有時可以點選SeekBar,但又不能拖拽問題,我們就可以從mOnTouchListener下手了。