Android 仿微信对话列表滑动删除效果

http://blog.csdn.net/top_code/article/details/17965743


微信对话列表滑动删除效果很不错的,借鉴了github上SwipeListView(项目地址:https://github.com/likebamboo/SwipeListView),在其上进行了一些重构,最终实现了微信对话列表滑动删除效果。


实现原理
1.通过ListView的pointToPosition(int x, int y)来获取按下的position,然后通过android.view.ViewGroup.getChildAt(position)来得到滑动对象swipeView
2.在onTouchEvent中计算要滑动的距离,调用swipeView.scrollTo即可。


运行效果如下







下面是最核心的部分SwipeListView代码:

[java] view plain copy
  1. package com.fxsky.swipelist.widget;  
  2.   
  3. import android.annotation.SuppressLint;  
  4. import android.content.Context;  
  5. import android.content.res.TypedArray;  
  6. import android.os.Handler;  
  7. import android.os.Message;  
  8. import android.util.AttributeSet;  
  9. import android.view.MotionEvent;  
  10. import android.view.View;  
  11. import android.widget.ListView;  
  12.   
  13. import com.fxsky.swipelist.R;  
  14.   
  15. public class SwipeListView extends ListView {  
  16.     private Boolean mIsHorizontal;  
  17.   
  18.     private View mPreItemView;  
  19.   
  20.     private View mCurrentItemView;  
  21.   
  22.     private float mFirstX;  
  23.   
  24.     private float mFirstY;  
  25.   
  26.     private int mRightViewWidth;  
  27.   
  28.     // private boolean mIsInAnimation = false;  
  29.     private final int mDuration = 100;  
  30.   
  31.     private final int mDurationStep = 10;  
  32.   
  33.     private boolean mIsShown;  
  34.   
  35.     public SwipeListView(Context context) {  
  36.         this(context,null);  
  37.     }  
  38.   
  39.     public SwipeListView(Context context, AttributeSet attrs) {  
  40.         this(context, attrs,0);  
  41.     }  
  42.   
  43.     public SwipeListView(Context context, AttributeSet attrs, int defStyle) {  
  44.         super(context, attrs, defStyle);  
  45.           
  46.         TypedArray mTypedArray = context.obtainStyledAttributes(attrs,    
  47.                 R.styleable.swipelistviewstyle);    
  48.           
  49.       //获取自定义属性和默认值    
  50.       mRightViewWidth = (int) mTypedArray.getDimension(R.styleable.swipelistviewstyle_right_width, 200);     
  51.         
  52.       mTypedArray.recycle();    
  53.     }  
  54.   
  55.     /** 
  56.      * return true, deliver to listView. return false, deliver to child. if 
  57.      * move, return true 
  58.      */  
  59.     @Override  
  60.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  61.         float lastX = ev.getX();  
  62.         float lastY = ev.getY();  
  63.         switch (ev.getAction()) {  
  64.             case MotionEvent.ACTION_DOWN:  
  65.                 mIsHorizontal = null;  
  66.                 System.out.println("onInterceptTouchEvent----->ACTION_DOWN");  
  67.                 mFirstX = lastX;  
  68.                 mFirstY = lastY;  
  69.                 int motionPosition = pointToPosition((int)mFirstX, (int)mFirstY);  
  70.   
  71.                 if (motionPosition >= 0) {  
  72.                     View currentItemView = getChildAt(motionPosition - getFirstVisiblePosition());  
  73.                     mPreItemView = mCurrentItemView;  
  74.                     mCurrentItemView = currentItemView;  
  75.                 }  
  76.                 break;  
  77.   
  78.             case MotionEvent.ACTION_MOVE:  
  79.                 float dx = lastX - mFirstX;  
  80.                 float dy = lastY - mFirstY;  
  81.   
  82.                 if (Math.abs(dx) >= 5 && Math.abs(dy) >= 5) {  
  83.                     return true;  
  84.                 }  
  85.                 break;  
  86.   
  87.             case MotionEvent.ACTION_UP:  
  88.             case MotionEvent.ACTION_CANCEL:  
  89.                 System.out.println("onInterceptTouchEvent----->ACTION_UP");  
  90.                 if (mIsShown && (mPreItemView != mCurrentItemView || isHitCurItemLeft(lastX))) {  
  91.                     System.out.println("1---> hiddenRight");  
  92.                     /** 
  93.                      * 情况一: 
  94.                      * 

     

  95.                      * 一个Item的右边布局已经显示, 
  96.                      * 

     

  97.                      * 这时候点击任意一个item, 那么那个右边布局显示的item隐藏其右边布局 
  98.                      */  
  99.                     hiddenRight(mPreItemView);  
  100.                 }  
  101.                 break;  
  102.         }  
  103.   
  104.         return super.onInterceptTouchEvent(ev);  
  105.     }  
  106.   
  107.     private boolean isHitCurItemLeft(float x) {  
  108.         return x < getWidth() - mRightViewWidth;  
  109.     }  
  110.   
  111.     /** 
  112.      * @param dx 
  113.      * @param dy 
  114.      * @return judge if can judge scroll direction 
  115.      */  
  116.     private boolean judgeScrollDirection(float dx, float dy) {  
  117.         boolean canJudge = true;  
  118.   
  119.         if (Math.abs(dx) > 30 && Math.abs(dx) > 2 * Math.abs(dy)) {  
  120.             mIsHorizontal = true;  
  121.             System.out.println("mIsHorizontal---->" + mIsHorizontal);  
  122.         } else if (Math.abs(dy) > 30 && Math.abs(dy) > 2 * Math.abs(dx)) {  
  123.             mIsHorizontal = false;  
  124.             System.out.println("mIsHorizontal---->" + mIsHorizontal);  
  125.         } else {  
  126.             canJudge = false;  
  127.         }  
  128.   
  129.         return canJudge;  
  130.     }  
  131.   
  132.     /** 
  133.      * return false, can't move any direction. return true, cant't move 
  134.      * vertical, can move horizontal. return super.onTouchEvent(ev), can move 
  135.      * both. 
  136.      */  
  137.     @Override  
  138.     public boolean onTouchEvent(MotionEvent ev) {  
  139.         float lastX = ev.getX();  
  140.         float lastY = ev.getY();  
  141.   
  142.         switch (ev.getAction()) {  
  143.             case MotionEvent.ACTION_DOWN:  
  144.                 System.out.println("---->ACTION_DOWN");  
  145.                 break;  
  146.   
  147.             case MotionEvent.ACTION_MOVE:  
  148.                 float dx = lastX - mFirstX;  
  149.                 float dy = lastY - mFirstY;  
  150.   
  151.                 // confirm is scroll direction  
  152.                 if (mIsHorizontal == null) {  
  153.                     if (!judgeScrollDirection(dx, dy)) {  
  154.                         break;  
  155.                     }  
  156.                 }  
  157.   
  158.                 if (mIsHorizontal) {  
  159.                     if (mIsShown && mPreItemView != mCurrentItemView) {  
  160.                         System.out.println("2---> hiddenRight");  
  161.                         /** 
  162.                          * 情况二: 
  163.                          * 

     

  164.                          * 一个Item的右边布局已经显示, 
  165.                          * 

     

  166.                          * 这时候左右滑动另外一个item,那个右边布局显示的item隐藏其右边布局 
  167.                          * 

     

  168.                          * 向左滑动只触发该情况,向右滑动还会触发情况五 
  169.                          */  
  170.                         hiddenRight(mPreItemView);  
  171.                     }  
  172.   
  173.                     if (mIsShown && mPreItemView == mCurrentItemView) {  
  174.                         dx = dx - mRightViewWidth;  
  175.                         System.out.println("======dx " + dx);  
  176.                     }  
  177.   
  178.                     // can't move beyond boundary  
  179.                     if (dx < 0 && dx > -mRightViewWidth) {  
  180.                         mCurrentItemView.scrollTo((int)(-dx), 0);  
  181.                     }  
  182.   
  183.                     return true;  
  184.                 } else {  
  185.                     if (mIsShown) {  
  186.                         System.out.println("3---> hiddenRight");  
  187.                         /** 
  188.                          * 情况三: 
  189.                          * 

     

  190.                          * 一个Item的右边布局已经显示, 
  191.                          * 

     

  192.                          * 这时候上下滚动ListView,那么那个右边布局显示的item隐藏其右边布局 
  193.                          */  
  194.                         hiddenRight(mPreItemView);  
  195.                     }  
  196.                 }  
  197.   
  198.                 break;  
  199.   
  200.             case MotionEvent.ACTION_UP:  
  201.             case MotionEvent.ACTION_CANCEL:  
  202.                 System.out.println("============ACTION_UP");  
  203.                 clearPressedState();  
  204.                 if (mIsShown) {  
  205.                     System.out.println("4---> hiddenRight");  
  206.                     /** 
  207.                      * 情况四: 
  208.                      * 

     

  209.                      * 一个Item的右边布局已经显示, 
  210.                      * 

     

  211.                      * 这时候左右滑动当前一个item,那个右边布局显示的item隐藏其右边布局 
  212.                      */  
  213.                     hiddenRight(mPreItemView);  
  214.                 }  
  215.   
  216.                 if (mIsHorizontal != null && mIsHorizontal) {  
  217.                     if (mFirstX - lastX > mRightViewWidth / 2) {  
  218.                         showRight(mCurrentItemView);  
  219.                     } else {  
  220.                         System.out.println("5---> hiddenRight");  
  221.                         /** 
  222.                          * 情况五: 
  223.                          * 

     

  224.                          * 向右滑动一个item,且滑动的距离超过了右边View的宽度的一半,隐藏之。 
  225.                          */  
  226.                         hiddenRight(mCurrentItemView);  
  227.                     }  
  228.   
  229.                     return true;  
  230.                 }  
  231.   
  232.                 break;  
  233.         }  
  234.   
  235.         return super.onTouchEvent(ev);  
  236.     }  
  237.   
  238.     private void clearPressedState() {  
  239.         // TODO current item is still has background, issue  
  240.         mCurrentItemView.setPressed(false);  
  241.         setPressed(false);  
  242.         refreshDrawableState();  
  243.         // invalidate();  
  244.     }  
  245.   
  246.     private void showRight(View view) {  
  247.         System.out.println("=========showRight");  
  248.   
  249.         Message msg = new MoveHandler().obtainMessage();  
  250.         msg.obj = view;  
  251.         msg.arg1 = view.getScrollX();  
  252.         msg.arg2 = mRightViewWidth;  
  253.         msg.sendToTarget();  
  254.   
  255.         mIsShown = true;  
  256.     }  
  257.   
  258.     private void hiddenRight(View view) {  
  259.         System.out.println("=========hiddenRight");  
  260.         if (mCurrentItemView == null) {  
  261.             return;  
  262.         }  
  263.         Message msg = new MoveHandler().obtainMessage();//  
  264.         msg.obj = view;  
  265.         msg.arg1 = view.getScrollX();  
  266.         msg.arg2 = 0;  
  267.   
  268.         msg.sendToTarget();  
  269.   
  270.         mIsShown = false;  
  271.     }  
  272.   
  273.     /** 
  274.      * show or hide right layout animation 
  275.      */  
  276.     @SuppressLint("HandlerLeak")  
  277.     class MoveHandler extends Handler {  
  278.         int stepX = 0;  
  279.   
  280.         int fromX;  
  281.   
  282.         int toX;  
  283.   
  284.         View view;  
  285.   
  286.         private boolean mIsInAnimation = false;  
  287.   
  288.         private void animatioOver() {  
  289.             mIsInAnimation = false;  
  290.             stepX = 0;  
  291.         }  
  292.   
  293.         @Override  
  294.         public void handleMessage(Message msg) {  
  295.             super.handleMessage(msg);  
  296.             if (stepX == 0) {  
  297.                 if (mIsInAnimation) {  
  298.                     return;  
  299.                 }  
  300.                 mIsInAnimation = true;  
  301.                 view = (View)msg.obj;  
  302.                 fromX = msg.arg1;  
  303.                 toX = msg.arg2;  
  304.                 stepX = (int)((toX - fromX) * mDurationStep * 1.0 / mDuration);  
  305.                 if (stepX < 0 && stepX > -1) {  
  306.                     stepX = -1;  
  307.                 } else if (stepX > 0 && stepX < 1) {  
  308.                     stepX = 1;  
  309.                 }  
  310.                 if (Math.abs(toX - fromX) < 10) {  
  311.                     view.scrollTo(toX, 0);  
  312.                     animatioOver();  
  313.                     return;  
  314.                 }  
  315.             }  
  316.   
  317.             fromX += stepX;  
  318.             boolean isLastStep = (stepX > 0 && fromX > toX) || (stepX < 0 && fromX < toX);  
  319.             if (isLastStep) {  
  320.                 fromX = toX;  
  321.             }  
  322.   
  323.             view.scrollTo(fromX, 0);  
  324.             invalidate();  
  325.   
  326.             if (!isLastStep) {  
  327.                 this.sendEmptyMessageDelayed(0, mDurationStep);  
  328.             } else {  
  329.                 animatioOver();  
  330.             }  
  331.         }  
  332.     }  
  333.   
  334.     public int getRightViewWidth() {  
  335.         return mRightViewWidth;  
  336.     }  
  337.   
  338.     public void setRightViewWidth(int mRightViewWidth) {  
  339.         this.mRightViewWidth = mRightViewWidth;  
  340.     }  
  341. }  


github上有另一个项目47deg / android-swipelistview,项目地址:https://github.com/47deg/android-swipelistview,效果如下:





Demo下载地址:http://download.csdn.net/detail/fx_sky/6820665


------------------------------------------------------------

Demo中SwipeAdapter源码中有一处由于粗心写错了,会导致向下滑动时出现数组越界异常,现更正如下:

[java] view plain copy
  1. @Override  
  2.     public int getCount() {  
  3. //      return 100;  
  4.         return data.size();  
  5.     }  





你可能感兴趣的:(android)