Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> ListView側滑刪除的實現,SlideDeleteListView,針對ScrollView嵌套ListView視圖和手勢沖突優化

ListView側滑刪除的實現,SlideDeleteListView,針對ScrollView嵌套ListView視圖和手勢沖突優化

編輯:關於Android編程

關於ListView側滑刪除這是個老話題,大多數APP都具有這樣類似的功能,對於一位Android初涉者來說,實現這樣的功能確實有一點難度,網上的實現方法也層出不窮,我仔細在網上翻了一下,居然看到了還有很多實現側滑的第三方依賴包,覺得有些無語,嘗試使用一番,大多數實現還是很好的,比我今天要說的好的多,當然也有劣質的包,這裡也就不多說了。既然是老話題,那麼沒有一點實現上的優勢,我也說不下去,這個優勢大概就是只要自定義一個ListView便可以實現側滑刪除功能,尤其是對ScrollView嵌套ListView試圖和手勢沖突的優化。嚴格意義上這個實現方式也是我以前在網上看到的,後來基於這個實現思想修改優化的。好了,開教程:

1.先假設一個ListView的Item子布局message_item.xml是這樣的:

 




    

        

        


        
    

    
前面的RelativeLayout裡的內容就是大家常見的ListView的Item視圖,後面的TextView就是我們主角刪除按鈕,這裡把它也作為Item的子布局內容了。從布局裡可以看出,刪除按鈕TextView已經被RelativeLayout擠到最右邊,而不在屏幕顯示區域內。此時該Item的長度實際長度是屏幕的長度+刪除的按鈕的長度(這裡是70dp)。

 

 

2.下面我們自定義ListView----->SlideDeleteListView 這裡注意一點,就是盡量不要在任何自定義View中傳入某布局,那麼以後修改或用於別的項目,其要求發生了一些變化,還要針對被改變的布局修改邏輯代碼,這是我個人的一種開發思想,大家聽聽就好了。

 

	/**
	 * 構造方法,實例化入口,初始化相關數據或實例
	 *
	 * @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

 

 

	/**
	 * 手勢操作
	 * 
	 * @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手勢事件

 

 

 

/**
	 * 手指按下邏輯
	 */
	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。

 

 

	/**
	 * 手指移動邏輯
	 */
	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顯示狀態邏輯。
    /**
     * 手指釋放邏輯
     */
    private void onActionUp(MotionEvent e) {
        //判斷手指釋放後,刪除按鈕是否已顯示超過其寬度的一半
        if (-params.leftMargin >= mBtnDelWidth / 2) {
            params.leftMargin = -mBtnDelWidth;
            isBtnDelShow = true;
        } else {
            //恢復滑動前的視圖狀態
            resetItemView();
        }
        itemViewGroup.getChildAt(0).setLayoutParams(params);
    }


 

 

    /**
     * 重寫該方法是用來應對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);
        }
    }

 

	/**
	 * 重置itemView,恢復原顯示狀態
	 */
	public void resetItemView() {
		params.leftMargin = 0;
		itemViewGroup.getChildAt(0).setLayoutParams(params);
		isBtnDelShow = false;
	}
看注釋。

 

 


3.在適配器Adapter中獲取該ListView對象,當刪除按鈕顯示時,點擊刪除,刪除集合裡對應的數據,ListView對象再調用上述的resetItemView()方法,再調用adapter的notifyDataSetChanged()方法更新界面。

 

holder.tv_btn_delete.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				items.remove(items.get(position));
				lv_messages.resetItemView();
				notifyDataSetChanged();
			}
		});

 

 

效果圖: \

OK,實現方式的核心代碼已貼上,如果還有什麼不懂的地方或有更好的建議歡迎留言。Demo源碼下載

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved