對于整個觸摸事件傳遞過程,我畫了簡要的流程圖,方便日后快速回顧。
單點觸摸,沒有考慮邊緣滑動檢測的最簡流程圖
單點觸摸,考慮了邊緣滑動檢測的流程圖
多點觸摸情況我就沒研究了,在這里忽略~
三個開啟自動滾動的方法:
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
SwipeBackLayout、SwipeBack
SlidingUpPanel
DrawerLayout
其他關(guān)于ViewDragHelper的分析文章
Each Navigation Drawer Hides a ViewDragHelper,文中的源碼就是上面的YoutubeLayout
ViewDragHelper詳解,這是上面文章的簡略中文版