Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android學習小Demo(18)Todo List 仿QQ刪除任務

Android學習小Demo(18)Todo List 仿QQ刪除任務

編輯:關於Android編程

一般情況下,如果想要在ListView上面實現Listitem的滑動刪除效果,或者仿QQ的滑動顯示刪除效果的時候,只需要繼承ListView,自定義一個ListView就可以,不過由於之前用了開源庫StickyListHeaders來實現ListView的分組,那麼這一次在實現這個仿QQ的左右滑動顯示隱藏刪除按鈕的時候,就要在StickyListHeaders的基礎上實現。而StickyListHeaders要開放了一個setOnTouchListener的接口,允許調用者傳進去一個OnTouchListener,來實現自定義的OnTouch效果,如下:

    @Override
    public void setOnTouchListener(final OnTouchListener l) {
        if (l != null) {
            mList.setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    return l.onTouch(StickyListHeadersListView.this, event);
                }
            });
        } else {
            mList.setOnTouchListener(null);
        }
    }

那麼解決問題的方法就在於實現一個OnTouchListener,然後將其傳給StickyListHeaderListView。

		lvTasks.setOnTouchListener(new ListitemSlopListener(this));

而ListitemSlopListener就是我們自定義的OnTouchListener了,其代碼如下:

public class ListitemSlopListener implements OnTouchListener {

	...
	private void initConfiguration() {
		ViewConfiguration vc = ViewConfiguration.get(mContext);
		mSlop = vc.getScaledTouchSlop();
		mMinFling = vc.getScaledMaximumFlingVelocity();
		mMaxFling = mMinFling * 8;
	}
	...
	@Override
	public boolean onTouch(View v, MotionEvent event) {				
		mListView = ((StickyListHeadersListView) v).getWrappedList();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:			
			mDownX = event.getX();
			mDownY = event.getY();				
			mPosition = mListView.pointToPosition((int)mDownX, (int)mDownY);
			mSlopView = mListView.getChildAt(mPosition - mListView.getFirstVisiblePosition());
			Log.v(Helper.TAG,"Action Down : onTouch Triggered");
			if(mSlopView != null){	
				btnDelete = (Button) mSlopView.findViewById(R.id.btnDelete);	
				if(btnDelete.getVisibility() == View.INVISIBLE || btnDelete.getVisibility() == View.GONE){
					btnDelete.setVisibility(View.VISIBLE);
					ViewHelper.setAlpha(btnDelete, 0);
				}
				mBtnWidth = btnDelete.getWidth();				
				initVelocityTrackerIfNotExists(event);
				return true;
			}
			break;
		case MotionEvent.ACTION_MOVE:			
			if(mVelocityTracker != null && mSlopView != null && btnDelete != null){				
				float dx = event.getX() - mDownX;
				float dy = event.getY() - mDownY;
				if(Math.abs(dx) > mSlop && Math.abs(dy) < mSlop){
					mMoving = true;		
				}
				if(mMoving){
					if (dx > 0) {
						//Direction : right to hide the button if the button shows
						if(ViewHelper.getAlpha(btnDelete) > 0){
							ViewHelper.setAlpha(
								btnDelete,Math.min(1f,Math.max(0f, 1f -  Math.abs(dx) /mBtnWidth)));
						}
					} else {
						//Direction : Left to show the button if the button exists
						ViewHelper.setAlpha(
								btnDelete,
								Math.max(0f,Math.min(1f, Math.abs(dx) / mBtnWidth)));						
					}
					return true;
				}else{					
					return false;
				}
			}
		case MotionEvent.ACTION_UP:					
			if (mVelocityTracker != null && mSlopView != null && mMoving) {
				float dx = event.getX() - mDownX;
				mVelocityTracker.computeCurrentVelocity(1000);
				float vx = Math.abs(mVelocityTracker.getXVelocity());
				float vy = Math.abs(mVelocityTracker.getYVelocity());
				
				boolean isShow = false;
				if(Math.abs(dx) > mBtnWidth / 2){
					isShow = dx < 0;					
				}else if(vx > mMinFling && vx < mMaxFling && vx > vy){
					isShow = vx < 0;
				}

				if(isShow){
					ViewPropertyAnimator.animate(btnDelete).alpha(1f).setDuration(DURATION);
				}else{
					ViewPropertyAnimator.animate(btnDelete).alpha(0f).setDuration(DURATION);
				}								
				recycleVelocityTracker();
				mMoving = false;
			}else{
				//Only When the Delete Button is not visible, the the onItemClick action was fired.
				if(btnDelete != null && ViewHelper.getAlpha(btnDelete) > 0){
					ViewPropertyAnimator.animate(btnDelete).alpha(0f).setDuration(DURATION);
				}else{
					TodoTask todoTask = (TodoTask) mListView.getItemAtPosition(mPosition);
					if(todoTask != null){
						mListView.performItemClick(mSlopView, mPosition, todoTask.getId());
					}
				}
			}			
			return true;
		}
		return false;
	}

}


我們知道,在屏幕上的任何事件,包括點擊,長按,滑動,還是其它手勢,其實都是由一系列的Touch事件組成的,而這些Touch事件其實是下面三步組成的:

0)一個ACTION_DOWN事件

1)N個ACTION_MOVE事件

2)一個ACTION_UP事件組成的。

而根據Android的事件分發機制,在觸發View本身的OnTouchEvent方法的時候,如果存在一個OnTouchListener,那麼View會首先調用OnTouchListener來響應事件,只有當OnTouchListener返回false,表明其不處理該事件的時候,才會繼續將事件傳遞給View的OnTouchEvent,那麼在這裡,我們就要在OnTouchListener的onTouch方法中,將這個事件給完全消費掉,來實現我們自己想要的效果。

OnTouch方法中,主要實現了下面的功能:

1)當我們手指接觸到屏幕上的時候,一個Down事件就會被觸發,於是會來到Listener的Down分支下面。

在這裡,我們會首先利用ListView的pointToPosition方法,根據觸摸的點坐標來獲取ListView中item的位置,並利用getChildAt方法找出手指觸摸到的那個Listitem,並將其放置到變量mSlopView中。

mSlopView其實就是TaskItem,其結構如下:




    

    

    

刪除按鈕一開始是invisible的,當Down事件發生的時候,我們要將其置為Visible的,但同時要將其alpha值置為0,因為雖然可見,但此時還不能顯示出來。

而最後,要在這裡返回一個true,表明在這裡,Down事件已經被這個OnTouchListener給消費了,這樣,後續的Move事件, Up事件才會繼續被這個OnTouchListener來處理。

2)當Donw事件被這個OnTouchListener消費後,後續的Move事件也會來到這裡進行處理。

在Move分支中, 我們要根據移動的距離來判斷這是不是一次移動,這是通過跟mSlop變量比較實現的。mSlop值是Android定義一個滑動事件的最短距離,當移動超過這個距離,表明這是一個滑動事件,那麼這個時候,我們就要根據移動的距離去動態地改變刪除按鈕的alpha值,來顯示或者隱藏刪除按鈕。在這裡,定義

2.1)當手指向左移動的時候,則表明這是要顯示按鈕,那麼會將按鈕的alpha值由 0 向 1 變化,最終完全顯示按鈕。

2.2)當手指向右移動的時候,則表明是要刪除按鈕,那麼會將按鈕的alpha值由 1 向 0 變化,最終完全隱藏按鈕。

最後,這個方法也要返回 true ,表明Move事件也在這裡被消費了。

3)最後就是來到Up事件了。每一個Listitem的OnTouch事件其實分兩種情況:

3.1)當OnTouch事件被定義為滑動事件時,即滑動距離超過mSlop的時候,我們要根據最終滑動的距離來決定是否要顯示按鈕。只有當滑動距離超過按鈕的一半寬度的時候,才顯示按鈕,如果按鈕還沒有完全顯示,則添加一個動畫效果,來顯示按鈕。而如果沒有超過按鈕的一半寬度的時候,也要為該按鈕添加一個動畫效果,但是這個效果則是慢慢慢將按鈕的alpha值變為0,最終隱藏按鈕。

3.1)當滑動的距離小於mSlop的時候,該事件只被定義為一個OnItemClick事件,那麼在這裡要顯示地調用performClick方法來觸發ListView的OnItemClick事件,這是因為不想去改變開源庫的源碼,所以我們必須將所有的OnTouch事件(當然,只是我們想處理的)都在OnTouchListener中處理。

最後,在Up分支中,我們處理了事件之後,同樣返回一個true,表明所有的OnTouch事件都在這裡被處理了,不需要再將事件傳遞下去。

下面是實現後的效果圖:


結束。

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