設計思路
- Item 內部用
Scroller 處理水平滑動(前景 + 背景刪除按鈕)。
ListView 攔截觸摸事件:判斷是垂直滾動列表還是水平滑動 Item。
- 點擊事件傳遞:
dispatchTouchEvent 中檢測 ACTION_UP 且未被消費,手動調用 performItemClick 觸發 OnItemClickListener。
Item View (ListItemDelete) 關鍵代碼
public class ListItemDelete extends LinearLayout {
private Scroller mScroller;
private float mLastMotionX, downX;
private int backWidth; // 背景視圖寬度(刪除按鈕區域)
@Override protected void onMeasure(...) { // 測量背景寬度
for (int i=0; i backWidth/2) smoothScrollTo(backWidth, 0); // 彈開
else smoothScrollTo(0, 0); // 彈回
return Math.abs(x - downX) >= 5; // 小於5視為點擊,返回false讓ListView處理
case MotionEvent.ACTION_CANCEL: smoothScrollTo(0, 0); break;
}
return true;
}
private void smoothScrollTo(int destX, int destY) {
mScroller.startScroll(getScrollX(), 0, destX - getScrollX(), 0, 300);
invalidate();
}
@Override public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
@Override protected void onLayout(...) { // 水平排列子View
int left = 0;
for (View v : children) if (v.getVisibility() != GONE) {
v.layout(left, 0, left + v.getMeasuredWidth(), v.getMeasuredHeight());
left += v.getMeasuredWidth();
}
}
}
ListView (ScrollListviewDelete) 觸摸分發
@Override public boolean onInterceptTouchEvent(MotionEvent ev) {
return isIntercept(ev); // 返回true攔截給Item,false給ListView滾動
}
@Override public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = super.dispatchTouchEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_UP && !handled) {
int pos = pointToPosition((int)ev.getX(), (int)ev.getY());
View v = getChildAt(pos);
performItemClick(v, pos, v.getId()); // 手動觸發ItemClick
}
return handled;
}
private boolean isIntercept(MotionEvent ev) {
float x = ev.getX(), y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: lastX=x; lastY=y; break;
case MotionEvent.ACTION_MOVE:
if (!isLock) {
if (Math.abs(x-lastX) > Math.abs(y-lastY) && Math.abs(x-lastX) > 10) {
isLock = true; return false; // 攔截給Item水平滑動
}
} else return false;
break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: isLock=false; break;
}
return true; // 默認不攔截,ListView垂直滾動
}
XML 佈局結構
<ListItemDelete ...>
<LinearLayout android:id="@+id/front" ...> <!-- 前景內容 --> </LinearLayout>
<LinearLayout ...> <!-- 背景刪除按鈕 -->
<Button android:id="@+id/btnDelete" ... />
</LinearLayout>
</ListItemDelete>
效果演示
