ViewDragHelper源碼解析總結(jié)

對于整個觸摸事件傳遞過程,我畫了簡要的流程圖,方便日后快速回顧。
單點觸摸,沒有考慮邊緣滑動檢測的最簡流程圖
單點觸摸,考慮了邊緣滑動檢測的流程圖

多點觸摸情況我就沒研究了,在這里忽略~
三個開啟自動滾動的方法:
settleCapturedViewAt(int finalLeft, int finalTop)以松手前的滑動速度為初速動,讓捕獲到的View自動滾動到指定位置。只能在Callback的onViewReleased()中調(diào)用。
flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop)以松手前的滑動速度為初速動,讓捕獲到的View在指定范圍內(nèi)fling。只能在Callback的onViewReleased()中調(diào)用。
smoothSlideViewTo(View child, int finalLeft, int finalTop)指定某個View自動滾動到指定的位置,初速度為0,可在任何地方調(diào)用。

Callback的各個方法總結(jié):
void onViewDragStateChanged(int state)拖動狀態(tài)改變時會調(diào)用此方法,狀態(tài)state有STATE_IDLE、STATE_DRAGGING、STATE_SETTLING三種取值。它在setDragState()里被調(diào)用,而setDragState()被調(diào)用的地方有tryCaptureViewForDrag()成功捕獲到子View時shouldInterceptTouchEvent()的ACTION_DOWN部分捕獲到
shouldInterceptTouchEvent()的ACTION_MOVE部分捕獲到
processTouchEvent()的ACTION_MOVE部分捕獲到

調(diào)用settleCapturedViewAt()、smoothSlideViewTo()、flingCapturedView()時
拖動View松手時(processTouchEvent()的ACTION_UP、ACTION_CANCEL)
自動滾動停止時(continueSettling()里檢測到滾動結(jié)束時)
外部調(diào)用abort()時

void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)正在被拖動的View或者自動滾動的View的位置改變時會調(diào)用此方法。在dragTo()里被調(diào)用(正在被拖動時)
在continueSettling()里被調(diào)用(自動滾動時)
外部調(diào)用abort()時被調(diào)用

void onViewCaptured(View capturedChild, int activePointerId)tryCaptureViewForDrag()成功捕獲到子View時會調(diào)用此方法。在shouldInterceptTouchEvent()的ACTION_DOWN里成功捕獲
在shouldInterceptTouchEvent()的ACTION_MOVE里成功捕獲
在processTouchEvent()的ACTION_MOVE里成功捕獲
手動調(diào)用captureChildView()

void onViewReleased(View releasedChild, float xvel, float yvel)拖動View松手時(processTouchEvent()的ACTION_UP)或被父View攔截事件時(processTouchEvent()的ACTION_CANCEL)會調(diào)用此方法。
void onEdgeTouched(int edgeFlags, int pointerId)ACTION_DOWN或ACTION_POINTER_DOWN事件發(fā)生時如果觸摸到監(jiān)聽的邊緣會調(diào)用此方法。edgeFlags的取值為EDGE_LEFT、EDGE_TOP、EDGE_RIGHT、EDGE_BOTTOM的組合。
boolean onEdgeLock(int edgeFlags)返回true表示鎖定edgeFlags對應(yīng)的邊緣,鎖定后的那些邊緣就不會在onEdgeDragStarted()被通知了,默認(rèn)返回false不鎖定給定的邊緣,edgeFlags的取值為EDGE_LEFT、EDGE_TOP、EDGE_RIGHT、EDGE_BOTTOM其中之一。
void onEdgeDragStarted(int edgeFlags, int pointerId)ACTION_MOVE事件發(fā)生時,檢測到開始在某些邊緣有拖動的手勢,也沒有鎖定邊緣,會調(diào)用此方法。edgeFlags取值為EDGE_LEFT、EDGE_TOP、EDGE_RIGHT、EDGE_BOTTOM的組合。可在此手動調(diào)用captureChildView()觸發(fā)從邊緣拖動子View的效果。
int getOrderedChildIndex(int index)在尋找當(dāng)前觸摸點下的子View時會調(diào)用此方法,尋找到的View會提供給tryCaptureViewForDrag()來嘗試捕獲。如果需要改變子View的遍歷查詢順序可改寫此方法,例如讓下層的View優(yōu)先于上層的View被選中。
int getViewHorizontalDragRange(View child)、int getViewVerticalDragRange(View child)返回給定的child在相應(yīng)的方向上可以被拖動的最遠(yuǎn)距離,默認(rèn)返回0。ACTION_DOWN發(fā)生時,若觸摸點處的child消費了事件,并且想要在某個方向上可以被拖動,就要在對應(yīng)方法里返回大于0的數(shù)。被調(diào)用的地方有三處:在checkTouchSlop()中被調(diào)用,返回值大于0才會去檢查mTouchSlop。在ACTION_MOVE里調(diào)用tryCaptureViewForDrag()之前會調(diào)用checkTouchSlop()。如果checkTouchSlop()失敗,就不會去捕獲View了。
如果ACTION_DOWN發(fā)生時,觸摸點處有子View消費事件,在shouldInterceptTouchEvent()的ACTION_MOVE里會被調(diào)用。如果兩個方向上的range都是0(兩個方法都返回0),就不會去捕獲View了。
在調(diào)用smoothSlideViewTo()時被調(diào)用,用于計算自動滾動要滾動多長時間,這個時間計算出來后,如果超過最大值,最終時間就取最大值,所以不用擔(dān)心在getView[Horizontal|Vertical]DragRange里返回了不合適的數(shù)導(dǎo)致計算的時間有問題,只要返回大于0的數(shù)就行了。

boolean tryCaptureView(View child, int pointerId)在tryCaptureViewForDrag()中被調(diào)用,返回true表示捕獲給定的child。tryCaptureViewForDrag()被調(diào)用的地方有shouldInterceptTouchEvent()的ACTION_DOWN里
shouldInterceptTouchEvent()的ACTION_MOVE里
processTouchEvent()的ACTION_MOVE里

int clampViewPositionHorizontal(View child, int left, int dx)、int clampViewPositionVertical(View child, int top, int dy)child在某方向上被拖動時會調(diào)用對應(yīng)方法,返回值是child移動過后的坐標(biāo)位置,clampViewPositionHorizontal()返回child移動過后的left值,clampViewPositionVertical()返回child移動過后的top值。兩個方法被調(diào)用的地方有兩處:在dragTo()中被調(diào)用,dragTo()在processTouchEvent()的ACTION_MOVE里被調(diào)用。用來獲取被拖動的View要移動到的位置。
如果ACTION_DOWN發(fā)生時,觸摸點處有子View消費事件,在shouldInterceptTouchEvent()的ACTION_MOVE里會被調(diào)用。如果兩個方向上返回的還是原來的left和top值,就不會去捕獲View了。

案例參考
在這里列舉一部分對ViewDragHelper的應(yīng)用案例,大家自己剖析它們的源碼來實踐鞏固。
YoutubeLayout,這是最簡單的Demo
QQ5.x側(cè)滑菜單ResideLayout
SwipeBackLayoutSwipeBack
SlidingUpPanel
DrawerLayout

其他關(guān)于ViewDragHelper的分析文章
Each Navigation Drawer Hides a ViewDragHelper,文中的源碼就是上面的YoutubeLayout
ViewDragHelper詳解,這是上面文章的簡略中文版

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內(nèi)容