最近在工做中,遇到須要自定義Listview Item的左右滑動效果,ListView Item自己有點擊事件,滑動過程當中item須要顯示不一樣於其餘的背景顏色,滑開以後,背景顏色保持而且又有item內的點擊事件這樣:java
說明這個問題呢,須要先說明一下這個效果的實現邏輯:app
一、根據ACTION_DOWN事件的按下位置,獲取ListView被按下位置的item,並設置按下背景顏色ide
二、根據ACTION_MOVE時間判斷滑動距離,若是滑動達到左右滑動閾值,那麼根據左右滑動距離,將View進行左右位置偏移,若是未達到閾值則不處理該事件。this
三、用戶可點擊整個item,觸發跳轉事件spa
四、View位置移動後,用戶點擊Delete按鈕,刪除該條itemrest
五、若是View已經被用戶移開,那麼若是再次觸發ACTION_DOWN,被移開的View自動恢復到原來位置。code
主要邏輯如上,效果實現不難,重寫ListView的onTouch事件就行了啊,可是在自測過程當中,發現一個大問題,就是在快速滑動ListView的時候,onTouch方法有事會丟失ACTION_DOWN事件。可是後面的Move事件和up事件歷來沒有丟失過。若是都丟失還好,可是MOVE和UP沒有丟失,就致使關鍵判斷丟失,又加上ListView item的複用機制,致使整個ListView狀態出錯,搞地整我的都很差了。事件
覺得是我本身寫的方法哪裏返回值有錯,就將全部本身的寫的東西註釋掉,而後在onTouchEvent裏面打印DOWN的狀態,發現就是沒有我代碼,DOWN事件同樣在快速滑動的時候接收不到。不是本身代碼的問題就好。而後Google了好幾下,發現問這個問題的很多,可是能解決個人問題方案沒有。就在我覺得這是一個不可解決的問題的時候,我驚恐的發現,QQ的item就不存在這個問題,每次劃開,再快的速度滑動,均可以正常關閉,我擦擦,不能輸啊。get
因而開始看源碼,細節就很少說,結論以下:源碼
該DOWN事件在ListView 的dispatchTouchEvent的時候是能夠接收到的,可是在onTouchEvent方法裏面就接收不到了。因此咱們就去看看ListView的dispatchTouchEvent方法,ListView的dispatchTouchEvent是在ViewGroup裏面,只看通過我分析關鍵的部分:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. cancelAndClearTouchTargets(ev); resetTouchState(); } // Check for interception. final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; } . . . }
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; }
關鍵就在這裏,咱們能夠看到有一句:// restore action in case it was changed,由於快速滑動,當DOWN事件傳遞到onInterceptTouchEvent方法去判斷是否是該攔截的時候,MOVE事件來了,而後被restore了。
因而在onInterceptTouchEvent方法裏面打印了一下是否有丟失DOWN事件,發現是:木有丟失!!!好噠,問題就在這裏了。瞭解問題在這裏,這個問題就容易解決了。重寫onInterceptTouchEvent,判斷DOWN事件的位置是否是須要攔截,返回正確的TRUE or FALSE值就OK了嘍。