轉載:http://blog.csdn.net/ausboyue/article/details/52565237的博客
關于ListView側滑刪除這是個老話題,大多數APP都具有這樣類似的功能,對于一位Android初涉者來說,實現這樣的功能確實有一點難度,網上的實現方法也層出不窮,我仔細在網上翻了一下,居然看到了還有很多實現側滑的第三方依賴包,覺得有些無語,嘗試使用一番,大多數實現還是很好的,比我今天要說的好的多,當然也有劣質的包,這里也就不多說了。既然是老話題,那么沒有一點實現上的優勢,我也說不下去,這個優勢大概就是只要自定義一個ListView便可以實現側滑刪除功能,尤其是對ScrollView嵌套ListView視圖和手勢沖突的優化。嚴格意義上這個實現方式也是我以前在網上看到的,后來基于這個實現思想修改優化的。好了,開教程:
1.先假設一個ListView的Item子布局message_item.xml是這樣的:
[html] view plain copy<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="62dp" android:background="@drawable/common_list_item_bg" android:gravity="center_vertical" android:minHeight="?android:attr/listPReferredItemHeight" android:orientation="horizontal"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/iv_icon_read_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="15dp" android:src="@drawable/ic_msg_unread" /> <TextView android:id="@+id/tv_msg_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="15dp" android:layout_toRightOf="@id/iv_icon_read_status" android:text="訂單預定成功;訂單號6607967" android:textColor="@color/common_text" android:textSize="14sp" /> <TextView android:id="@+id/tv_msg_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="15dp" android:text="前天 13:21" android:textColor="@color/text_smoke" android:textSize="12sp" /> </RelativeLayout> <TextView android:id="@+id/tv_btn_delete" android:layout_width="70dp" android:layout_height="match_parent" android:background="@color/red" android:clickable="true" android:gravity="center" android:text="刪除" android:textColor="@color/white" android:textSize="17sp" /> </LinearLayout> 前面的RelativeLayout里的內容就是大家常見的ListView的Item視圖,后面的TextView就是我們主角刪除按鈕,這里把它也作為Item的子布局內容了。從布局里可以看出,刪除按鈕TextView已經被RelativeLayout擠到最右邊,而不在屏幕顯示區域內。此時該Item的長度實際長度是屏幕的長度+刪除的按鈕的長度(這里是70dp)。2.下面我們自定義ListView----->SlideDeleteListView 這里注意一點,就是盡量不要在任何自定義View中傳入某布局,那么以后修改或用于別的項目,其要求發生了一些變化,還要針對被改變的布局修改邏輯代碼,這是我個人的一種開發思想,大家聽聽就好了。
[java] view plain copy/** * 構造方法,實例化入口,初始化相關數據或實例 * * @param context * @param attrs * @param defStyleAttr */ public SlideDeleteListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 窗口管理器 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); // 新建顯示度量尺 DisplayMetrics metrics = new DisplayMetrics(); // 對度量尺進行包裝,附參 wm.getDefaultDisplay().getMetrics(metrics); // 初始化屏幕寬度參數 mSreeenWidth = metrics.widthPixels; }SlideDeleteListView構造方法中獲取屏幕寬度mSreeenWidth
[java] view plain copy/** * 手勢操作 * * @param ev * @return */ @SuppressLint("ClickableViewaccessibility") @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN:// 按壓 onActionDowm(ev); break; case MotionEvent.ACTION_MOVE:// 移動 return onActionMove(ev); case MotionEvent.ACTION_UP:// 釋放 onActionUp(ev); break; } return super.onTouchEvent(ev); } 重寫SlideDeleteListView手勢事件[java] view plain copy/** * 手指按下邏輯 */ private void onActionDowm(MotionEvent e) { if (isBtnDelShow) { resetItemView(); } mDownX = (int) e.getX(); mDownY = (int) e.getY(); // 獲得被按下位置的item Integer currentPosition = pointToPosition(mDownX, mDownY); if (-1 == currentPosition) { return; } itemViewGroup = (ViewGroup) getChildAt(currentPosition - getFirstVisiblePosition()); // 獲得刪除按鈕的寬度,刪除按鈕屬于第二個子View(上述布局中能看得出來),position為1 mBtnDelWidth = itemViewGroup.getChildAt(1).getLayoutParams().width; /* 將第一個子View也就是我們常見的Item顯示的View的寬固定為屏幕同寬度 */ params = (LinearLayout.LayoutParams) itemViewGroup.getChildAt(0).getLayoutParams(); params.width = mSreeenWidth; itemViewGroup.getChildAt(0).setLayoutParams(params); }手指按下的時候,把剛才那個item常見顯示的視圖RelativeLayout寬度成屏幕的寬度,以及獲得刪除按鈕TextView 的寬度,isBtnDelShow為flag,用于標記刪除是否處于顯示狀態,若顯示,點擊時重置下Item顯示狀態(即不顯示刪除按鈕的視圖狀態),Integer currentPosition = pointToPosition(mDownX, mDownY),currentPosition 為-1時表示手指點擊點是在item之間的分割線上,不作邏輯處理。itemViewGroup即Item的布局,itemViewGroup.getChildAt(0)為Item子View,即上述的RelativeLayout。
[java] view plain copy/** * 手指移動邏輯 */ private boolean onActionMove(MotionEvent e) { int nowX = (int) e.getX(); int nowY = (int) e.getY(); // 判斷是否為偏向左右的滑動 if (Math.abs(nowX - mDownX) > Math.abs(nowY - mDownY)) { // 左右滑動請求消費該事件,防止上下滑動以及被ScrollView嵌套的手勢沖突 requestDisallowInterceptTouchEvent(true); // 判斷是否為向左滑動 if (nowX < mDownX) { int srollX = mDownX - nowX; // 判斷左滑距離是否超過刪除按鈕寬 if (srollX >= mBtnDelWidth) { srollX = mBtnDelWidth; } params.leftMargin = -srollX; itemViewGroup.getChildAt(0).setLayoutParams(params); } // 消費掉該移動事件 return true; } return super.onTouchEvent(e); } 這里的思路就是判定左滑時,并根據左滑的絕對距離(即手指向左邊滑動的實際水平距離),實時設定RelativeLayout視圖的MarginLeft為相應距離的負值以達到感覺item布局像是被手指劃走的效果,刪除按鈕也隨即從左邊逐漸顯示出來。注意下requestDisallowInterceptTouchEvent(true)這行代碼的注釋,手指點擊的位置是在ListView上,且是左右滑,為了避免手勢沖突,不讓父View即ScrollView攔截該手勢事件。手指釋放時判斷向左滑動的距離,做顯示按鈕或重置最初的Item顯示狀態邏輯。[java] view plain copy/** * 手指釋放邏輯 */ private void onActionUp(MotionEvent e) { //判斷手指釋放后,刪除按鈕是否已顯示超過其寬度的一半 if (-params.leftMargin >= mBtnDelWidth / 2) { params.leftMargin = -mBtnDelWidth; isBtnDelShow = true; } else { //恢復滑動前的視圖狀態 resetItemView(); } itemViewGroup.getChildAt(0).setLayoutParams(params); } [java] view plain copy /** * 重寫該方法是用來應對ScrollView嵌套顯示不全的問題 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 獲得ScrollView或其子類對象,這里視情況而定,可能不需要只需要一個getParent()或多次,視自己的布局層次而定 Object object = getParent().getParent(); if (object instanceof ScrollView) {// 是ScrollView或其子類 /*解決與ScrollView的布局沖突,讓ListView完全顯示*/ int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } else { // 沒有ScrollView嵌套,正常super的方法 super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } [java] view plain copy/** * 重置itemView,恢復原顯示狀態 */ public void resetItemView() { params.leftMargin = 0; itemViewGroup.getChildAt(0).setLayoutParams(params); isBtnDelShow = false; } 看注釋。3.在適配器Adapter中獲取該ListView對象,當刪除按鈕顯示時,點擊刪除,刪除集合里對應的數據,ListView對象再調用上述的resetItemView()方法,再調用adapter的notifyDataSetChanged()方法更新界面。
[java] view plain copyholder.tv_btn_delete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { items.remove(items.get(position)); lv_messages.resetItemView(); notifyDataSetChanged(); } });效果圖:          
Demo源碼下載
新聞熱點
疑難解答