最近在工作中,遇到需要自定義Listview Item的左右滑動(dòng)效果,ListView Item本身有點(diǎn)擊事件,滑動(dòng)過(guò)程中item需要顯示不同于其他的背景顏色,滑開(kāi)之后,背景顏色保持并且又有item內(nèi)的點(diǎn)擊事件這樣:

說(shuō)明這個(gè)問(wèn)題呢,需要先說(shuō)明一下這個(gè)效果的實(shí)現(xiàn)邏輯:
1、根據(jù)ACTION_DOWN事件的按下位置,獲取ListView被按下位置的item,并設(shè)置按下背景顏色
2、根據(jù)ACTION_MOVE時(shí)間判斷滑動(dòng)距離,如果滑動(dòng)達(dá)到左右滑動(dòng)閾值,那么根據(jù)左右滑動(dòng)距離,將View進(jìn)行左右位置偏移,如果未達(dá)到閾值則不處理該事件。
3、用戶可點(diǎn)擊整個(gè)item,觸發(fā)跳轉(zhuǎn)事件
4、View位置移動(dòng)后,用戶點(diǎn)擊Delete按鈕,刪除該條item
5、如果View已經(jīng)被用戶移開(kāi),那么如果再次觸發(fā)ACTION_DOWN,被移開(kāi)的View自動(dòng)恢復(fù)到原來(lái)位置。
主要邏輯如上,效果實(shí)現(xiàn)不難,重寫(xiě)ListView的onTouch事件就好了啊,但是在自測(cè)過(guò)程中,發(fā)現(xiàn)一個(gè)大問(wèn)題,就是在快速滑動(dòng)ListView的時(shí)候,onTouch方法有事會(huì)丟失ACTION_DOWN事件。但是后面的Move事件和up事件從來(lái)沒(méi)有丟失過(guò)。如果都丟失還好,但是MOVE和UP沒(méi)有丟失,就導(dǎo)致關(guān)鍵判斷丟失,又加上ListView item的復(fù)用機(jī)制,導(dǎo)致整個(gè)ListView狀態(tài)出錯(cuò),搞地整個(gè)人都不好了。
以為是我自己寫(xiě)的方法哪里返回值有錯(cuò),就將所有自己的寫(xiě)的東西注釋掉,然后在onTouchEvent里面打印DOWN的狀態(tài),發(fā)現(xiàn)就是沒(méi)有我代碼,DOWN事件一樣在快速滑動(dòng)的時(shí)候接收不到。不是自己代碼的問(wèn)題就好。然后Google了好幾下,發(fā)現(xiàn)問(wèn)這個(gè)問(wèn)題的不少,但是能解決我的問(wèn)題方案沒(méi)有。就在我以為這是一個(gè)不可解決的問(wèn)題的時(shí)候,我驚恐的發(fā)現(xiàn),QQ的item就不存在這個(gè)問(wèn)題,每次劃開(kāi),再快的速度滑動(dòng),都可以正常關(guān)閉,我擦擦,不能輸啊。
于是開(kāi)始看源碼,細(xì)節(jié)就不多說(shuō),結(jié)論如下:
該DOWN事件在ListView 的dispatchTouchEvent的時(shí)候是可以接收到的,但是在onTouchEvent方法里面就接收不到了。所以我們就去看看ListView的dispatchTouchEvent方法,ListView的dispatchTouchEvent是在ViewGroup里面,只看經(jīng)過(guò)我分析關(guān)鍵的部分:
@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;
}
關(guān)鍵就在這里,我們可以看到有一句:// restore action in case it was changed,因?yàn)榭焖倩瑒?dòng),當(dāng)DOWN事件傳遞到onInterceptTouchEvent方法去判斷是不是該攔截的時(shí)候,MOVE事件來(lái)了,然后被restore了。
于是在onInterceptTouchEvent方法里面打印了一下是否有丟失DOWN事件,發(fā)現(xiàn)是:木有丟失?。?!好噠,問(wèn)題就在這里了。了解問(wèn)題在這里,這個(gè)問(wèn)題就容易解決了。重寫(xiě)onInterceptTouchEvent,判斷DOWN事件的位置是不是需要攔截,返回正確的TRUE or FALSE值就OK了嘍。
原文:https://my.oschina.net/zhibuji/blog/525488