Android實現類似QQ的滑動刪除效果

觀察QQ的滑動刪除效果,可以猜測可以滑動刪除的部分主要包含兩個部分,一個是內容區域(用於放置正常顯示的view),另一個是操作區域(用於放置刪除按鈕)。默認情況下,操作區域是不顯示的,內容區域的大小是填充整個容器,操作區域始終位於內容區域的右面。當開始滑動的時候,整個容器中的所有子view都像左滑動,如果操作區域此時是不可見的,設置爲可見。

我的實現思路就是自定義一個layout SwipeLayout繼承自FrameLayout。SwipeLayout包含兩個子view,第一個子view是內容區域,第二個子view是操作區域。滑動效果的控制,主要就是通過檢測SwipeLayout的touch事件來實現,這裏我不想自己去通過監聽touch事件來實現滑動效果,那是一個很繁瑣的過程。Android support庫裏其實已經提供了一個很好的工具類來幫我們做這件事情ViewDragHelper。如果你看過Android原生的DrawerLayout的代碼,就會發現DrawerLayout的滑動效果也是通過ViewDragHelper類實現的。

下面先介紹一下ViewDragHelper類的使用。

首先需要在容器中創建一個ViewDragHelper類的對象。

[java] view plaincopy
  1. mDragHelper = ViewDragHelper.create(this1.0f, new ViewDragHelper.Callback());  
接下來要把容器的事件處理委託給ViewDragHelper對象
[java] view plaincopy
  1. @Override  
  2. public boolean onInterceptTouchEvent(MotionEvent event) {  
  3.     if (mDragHelper.shouldInterceptTouchEvent(event)) {  
  4.             return true;  
  5.     }  
  6.     return super.onInterceptTouchEvent(event);  
  7. }  
  8.   
  9. @Override  
  10. public boolean onTouchEvent(MotionEvent event) {  
  11.     mDragHelper.processTouchEvent(event);  
  12.     return true;  
  13. }  

ViewDragHelper對象來決定motion event是否是屬於拖動過程。如果motion event屬於拖動過程,那麼觸摸事件就交給ViewDragHelper來處理,ViewDragHelper在處理拖動過程的時候,會調用ViewDragHelper.Callback對象的一系列方法。
我們可以通過ViewDragHelper.Callback來監聽以下幾種事件:
1.拖動的狀態改變
2.被拖動的view的位置改變
3.被拖動的view被放開的時間和位置

ViewDragHelper.Callback還提供了幾個方法用來影響拖動過程。
1.控制view可以拖動的範圍
2.確定某個view是否可以拖動

好了,直接看代碼分析吧。

在SwipeLayout的inflate事件中,獲取到contentView和actionView。

[java] view plaincopy
  1. @Override  
  2.     protected void onFinishInflate() {  
  3.         contentView = getChildAt(0);  
  4.         actionView = getChildAt(1);  
  5.         actionView.setVisibility(GONE);  
  6.     }  

在SwipeLayout的measure事件中,設置拖動的距離爲actionView的寬度。

[java] view plaincopy
  1. @Override  
  2.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  4.   
  5.         dragDistance = actionView.getMeasuredWidth();  
  6.     }  
定義DragHelperCallback extends ViewDragHelper.Callback


DragHelperCallback的tryCaptureView方法,用來確定contentView和actionView是可以拖動的
[java] view plaincopy
  1. @Override  
  2.     public boolean tryCaptureView(View view, int i) {  
  3.         return view == contentView || view == actionView;  
  4.     }  
DragHelperCallback的onViewPositionChanged在被拖動的view位置改變的時候調用,如果被拖動的view是contentView,我們需要在這裏更新actionView的位置,反之亦然。
[java] view plaincopy
  1. @Override  
  2.     public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {  
  3.         draggedX = left;  
  4.         if (changedView == contentView) {  
  5.             actionView.offsetLeftAndRight(dx);  
  6.         } else {  
  7.             contentView.offsetLeftAndRight(dx);  
  8.         }  
  9.         if (actionView.getVisibility() == View.GONE) {  
  10.             actionView.setVisibility(View.VISIBLE);  
  11.         }  
  12.         invalidate();  
  13.     }  
DragHelperCallback的clampViewPositionHorizontal用來限制view在x軸上拖動,要實現水平拖動效果必須要實現這個方法,我們這裏因爲僅僅需要實現水平拖動,所以沒有實現clampViewPositionVertical方法。

[java] view plaincopy
  1. @Override  
  2.   public int clampViewPositionHorizontal(View child, int left, int dx) {  
  3.       if (child == contentView) {  
  4.           final int leftBound = getPaddingLeft();  
  5.           final int minLeftBound = -leftBound - dragDistance;  
  6.           final int newLeft = Math.min(Math.max(minLeftBound, left), 0);  
  7.           return newLeft;  
  8.       } else {  
  9.           final int minLeftBound = getPaddingLeft() + contentView.getMeasuredWidth() - dragDistance;  
  10.           final int maxLeftBound = getPaddingLeft() + contentView.getMeasuredWidth() + getPaddingRight();  
  11.           final int newLeft = Math.min(Math.max(left, minLeftBound), maxLeftBound);  
  12.           return newLeft;  
  13.       }  
  14.   }  
DragHelperCallback的getViewHorizontalDragRange方法用來限制view可以拖動的範圍

[java] view plaincopy
  1. @Override  
  2.     public int getViewHorizontalDragRange(View child) {  
  3.         return dragDistance;  
  4.     }  
DragHelperCallback的onViewReleased方法中,根據滑動手勢的速度以及滑動的距離來確定是否顯示actionView。smoothSlideViewTo方法用來在滑動手勢之後實現慣性滑動效果

[java] view plaincopy
  1. @Override  
  2.     public void onViewReleased(View releasedChild, float xvel, float yvel) {  
  3.         super.onViewReleased(releasedChild, xvel, yvel);  
  4.         boolean settleToOpen = false;  
  5.         if (xvel > AUTO_OPEN_SPEED_LIMIT) {  
  6.             settleToOpen = false;  
  7.         } else if (xvel < -AUTO_OPEN_SPEED_LIMIT) {  
  8.             settleToOpen = true;  
  9.         } else if (draggedX <= -dragDistance / 2) {  
  10.             settleToOpen = true;  
  11.         } else if (draggedX > -dragDistance / 2) {  
  12.             settleToOpen = false;  
  13.         }  
  14.   
  15.         final int settleDestX = settleToOpen ? -dragDistance : 0;  
  16.         viewDragHelper.smoothSlideViewTo(contentView, settleDestX, 0);  
  17.         ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);  
  18.     }  
最終的效果:

代碼下載
github地址:https://github.com/lzyzsd/SwipeLayout

原文:http://blog.csdn.net/lzyzsd/article/details/41492783

發佈了27 篇原創文章 · 獲贊 111 · 訪問量 58萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章