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

微信对话列表滑动删除效果很不错的,借鉴了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代码:

package com.fxsky.swipelist.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;

import com.fxsky.swipelist.R;

public class SwipeListView extends ListView {
    private Boolean mIsHorizontal;

    private View mPreItemView;

    private View mCurrentItemView;

    private float mFirstX;

    private float mFirstY;

    private int mRightViewWidth;

    // private boolean mIsInAnimation = false;
    private final int mDuration = 100;

    private final int mDurationStep = 10;

    private boolean mIsShown;

    public SwipeListView(Context context) {
        this(context,null);
    }

    public SwipeListView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public SwipeListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs,  
                R.styleable.swipelistviewstyle);  
        
      //获取自定义属性和默认值  
      mRightViewWidth = (int) mTypedArray.getDimension(R.styleable.swipelistviewstyle_right_width, 200);   
      
      mTypedArray.recycle();  
    }

    /**
     * return true, deliver to listView. return false, deliver to child. if
     * move, return true
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        float lastX = ev.getX();
        float lastY = ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mIsHorizontal = null;
                System.out.println("onInterceptTouchEvent----->ACTION_DOWN");
                mFirstX = lastX;
                mFirstY = lastY;
                int motionPosition = pointToPosition((int)mFirstX, (int)mFirstY);

                if (motionPosition >= 0) {
                    View currentItemView = getChildAt(motionPosition - getFirstVisiblePosition());
                    mPreItemView = mCurrentItemView;
                    mCurrentItemView = currentItemView;
                }
                break;

            case MotionEvent.ACTION_MOVE:
                float dx = lastX - mFirstX;
                float dy = lastY - mFirstY;

                if (Math.abs(dx) >= 5 && Math.abs(dy) >= 5) {
                    return true;
                }
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                System.out.println("onInterceptTouchEvent----->ACTION_UP");
                if (mIsShown && (mPreItemView != mCurrentItemView || isHitCurItemLeft(lastX))) {
                    System.out.println("1---> hiddenRight");
                    /**
                     * 情况一:
                     * 

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

* 这时候点击任意一个item, 那么那个右边布局显示的item隐藏其右边布局 */ hiddenRight(mPreItemView); } break; } return super.onInterceptTouchEvent(ev); } private boolean isHitCurItemLeft(float x) { return x < getWidth() - mRightViewWidth; } /** * @param dx * @param dy * @return judge if can judge scroll direction */ private boolean judgeScrollDirection(float dx, float dy) { boolean canJudge = true; if (Math.abs(dx) > 30 && Math.abs(dx) > 2 * Math.abs(dy)) { mIsHorizontal = true; System.out.println("mIsHorizontal---->" + mIsHorizontal); } else if (Math.abs(dy) > 30 && Math.abs(dy) > 2 * Math.abs(dx)) { mIsHorizontal = false; System.out.println("mIsHorizontal---->" + mIsHorizontal); } else { canJudge = false; } return canJudge; } /** * return false, can't move any direction. return true, cant't move * vertical, can move horizontal. return super.onTouchEvent(ev), can move * both. */ @Override public boolean onTouchEvent(MotionEvent ev) { float lastX = ev.getX(); float lastY = ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("---->ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: float dx = lastX - mFirstX; float dy = lastY - mFirstY; // confirm is scroll direction if (mIsHorizontal == null) { if (!judgeScrollDirection(dx, dy)) { break; } } if (mIsHorizontal) { if (mIsShown && mPreItemView != mCurrentItemView) { System.out.println("2---> hiddenRight"); /** * 情况二: *

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

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

* 向左滑动只触发该情况,向右滑动还会触发情况五 */ hiddenRight(mPreItemView); } if (mIsShown && mPreItemView == mCurrentItemView) { dx = dx - mRightViewWidth; System.out.println("======dx " + dx); } // can't move beyond boundary if (dx < 0 && dx > -mRightViewWidth) { mCurrentItemView.scrollTo((int)(-dx), 0); } return true; } else { if (mIsShown) { System.out.println("3---> hiddenRight"); /** * 情况三: *

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

* 这时候上下滚动ListView,那么那个右边布局显示的item隐藏其右边布局 */ hiddenRight(mPreItemView); } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: System.out.println("============ACTION_UP"); clearPressedState(); if (mIsShown) { System.out.println("4---> hiddenRight"); /** * 情况四: *

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

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

* 向右滑动一个item,且滑动的距离超过了右边View的宽度的一半,隐藏之。 */ hiddenRight(mCurrentItemView); } return true; } break; } return super.onTouchEvent(ev); } private void clearPressedState() { // TODO current item is still has background, issue mCurrentItemView.setPressed(false); setPressed(false); refreshDrawableState(); // invalidate(); } private void showRight(View view) { System.out.println("=========showRight"); Message msg = new MoveHandler().obtainMessage(); msg.obj = view; msg.arg1 = view.getScrollX(); msg.arg2 = mRightViewWidth; msg.sendToTarget(); mIsShown = true; } private void hiddenRight(View view) { System.out.println("=========hiddenRight"); if (mCurrentItemView == null) { return; } Message msg = new MoveHandler().obtainMessage();// msg.obj = view; msg.arg1 = view.getScrollX(); msg.arg2 = 0; msg.sendToTarget(); mIsShown = false; } /** * show or hide right layout animation */ @SuppressLint("HandlerLeak") class MoveHandler extends Handler { int stepX = 0; int fromX; int toX; View view; private boolean mIsInAnimation = false; private void animatioOver() { mIsInAnimation = false; stepX = 0; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (stepX == 0) { if (mIsInAnimation) { return; } mIsInAnimation = true; view = (View)msg.obj; fromX = msg.arg1; toX = msg.arg2; stepX = (int)((toX - fromX) * mDurationStep * 1.0 / mDuration); if (stepX < 0 && stepX > -1) { stepX = -1; } else if (stepX > 0 && stepX < 1) { stepX = 1; } if (Math.abs(toX - fromX) < 10) { view.scrollTo(toX, 0); animatioOver(); return; } } fromX += stepX; boolean isLastStep = (stepX > 0 && fromX > toX) || (stepX < 0 && fromX < toX); if (isLastStep) { fromX = toX; } view.scrollTo(fromX, 0); invalidate(); if (!isLastStep) { this.sendEmptyMessageDelayed(0, mDurationStep); } else { animatioOver(); } } } public int getRightViewWidth() { return mRightViewWidth; } public void setRightViewWidth(int mRightViewWidth) { this.mRightViewWidth = mRightViewWidth; } }



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





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


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

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

@Override
    public int getCount() {
//      return 100;
        return data.size();
    }










你可能感兴趣的:(Android)