仿QQ微信向左滑動點擊刪除條目的經典案例

這裏寫圖片描述
本篇文章主要介紹如何側滑刪除條目的案例:
首先我們需要做一個自定義的recyclerView
1,用到的東西有,onTouch事件,觸屏事件跟蹤VelocityTracker,滑動view.scrollTo和scrollBy,Scroller的使用。
整個過程主要是對MotionEvent的三種狀態:
1,down
判定當前條目狀態,如果完全打開則立即關閉返回,如果關閉狀態,則根據getX()和getY()獲取當前位置拿到當前item條目。
2,move
判斷是否爲橫向滑動
判斷移動的距離是否超過左邊界和右邊界。
3,up
判斷速率超過1s內滑動100個像素。超過則關閉或完全打開(向左滑動速度爲負數,右滑動爲正數)
判定二:滑動的距離超過刪除按鈕一半長度,則完全滑出來,否則關閉。
話不多說上代碼:

 package com.hitv.recyclerview;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import android.widget.Scroller;
import android.widget.TextView;


public class SwipeRecyclerview extends RecyclerView {
    private Context mContext;

    //上一次的觸摸點
    private int mLastX, mLastY;
    //當前觸摸的item的位置
    private int mPosition;

    //item對應的佈局
    private LinearLayout mItemLayout;
    //刪除按鈕
    private TextView mDelete;

    //最大滑動距離(即刪除按鈕的寬度)
    private int mMaxLength;

    //item是在否跟隨手指移動
    private boolean isItemMoving;

    //item是否開始自動滑動
    private boolean isStartScroll;
    //刪除按鈕狀態   0:關閉 1:將要關閉 2:將要打開 3:打開
    private int mDeleteBtnState;

    private int scrollY = 0;
    //檢測手指在滑動過程中的速度
    private VelocityTracker mVelocityTracker;
    private Scroller mScroller;
    private OnItemDeleteListener mListener;
    private OnListItemClickListener mOnListItemClickListener;
    private static final String TAG = "SwipeRecyclerview";
    private float mDownY;
    private float mDownX;

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

    public SwipeRecyclerview(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwipeRecyclerview(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        mScroller = new Scroller(context, new LinearInterpolator());
        mVelocityTracker = VelocityTracker.obtain();
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        mVelocityTracker.addMovement(e);
        int x = (int) e.getX();
        int y = (int) e.getY();
        Log.d(TAG, "onTouchEvent: mask"+e.getActionMasked());
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownX = e.getX();
                mDownY = e.getY();
                if (mDeleteBtnState == 0) {
                    View view = findChildViewUnder(x, y);
                    Log.d(TAG, "onTouchEvent: "+view);
                    if (view == null) {
                        return false;
                    }
                    scrollY =  getScrollYDistance();
                    EventAdapter.EventHolder viewHolder = (EventAdapter.EventHolder) getChildViewHolder(view);

                    mItemLayout = viewHolder.mLayout;
                    mPosition = viewHolder.getAdapterPosition();
                    mDelete = viewHolder.mTxtDelete;
                    mMaxLength = mDelete.getWidth();
                    mDelete.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            mListener.onDeleteClick(mPosition);
                            mItemLayout.scrollTo(0, 0);
                            mDeleteBtnState = 0;
                        }
                    });
                } else if (mDeleteBtnState == 3){
                    mScroller.startScroll(mItemLayout.getScrollX(), 0, -mMaxLength, 0, 200);
                    invalidate();
                    mDeleteBtnState = 0;
                    return false;
                }else{
                    return false;
                }

                break;
            case MotionEvent.ACTION_MOVE:
                int dx = mLastX - x; //需要滑動的距離,每次滑動的距離
                int dy = mLastY - y;
                int scrollX = mItemLayout.getScrollX();//表示的是座標原點-它的座標位置
                Log.d(TAG, "onTouchEvent: mLastX= "+mLastX+" ,x="+x+" ,mLastY="+mLastY+" ,y="+y+" ,scrollX="+scrollX+" ,daxiao="+(scrollX + dx));
                if (Math.abs(dx) > Math.abs(dy)) {//左邊界檢測
                    isItemMoving = true;
                    if (scrollX + dx <= 0) {
                        mItemLayout.scrollTo(0, 0);
                        return true;
                    } else if (scrollX + dx >= mMaxLength) {//右邊界檢測
                        mItemLayout.scrollTo(mMaxLength, 0);
                        return true;
                    }
                    mItemLayout.scrollBy(dx, 0); //item跟隨手指滑動
                }
                break;
            case MotionEvent.ACTION_UP:
                float upX = e.getX();
                float upY = e.getY();
                mVelocityTracker.computeCurrentVelocity(1000);//計算手指滑動的速度
                float xVelocity = mVelocityTracker.getXVelocity();//水平方向速度(向左爲負)
                float yVelocity = mVelocityTracker.getYVelocity();//垂直方向速度

                int deltaX = 0;
                int upScrollX = mItemLayout.getScrollX();

                if (Math.abs(xVelocity) > 100 && Math.abs(xVelocity) > Math.abs(yVelocity)) {
                    if (xVelocity <= -100) {//左滑速度大於100,則刪除按鈕顯示
                        deltaX = mMaxLength - upScrollX;
                        mDeleteBtnState = 2;
                    } else if (xVelocity > 100) {//右滑速度大於100,則刪除按鈕隱藏
                        deltaX = -upScrollX;
                        mDeleteBtnState = 1;
                    }
                } else {
                    if (upScrollX >= mMaxLength / 2) {//item的左滑動距離大於刪除按鈕寬度的一半,則則顯示刪除按鈕
                        deltaX = mMaxLength - upScrollX;
                        mDeleteBtnState = 2;
                    } else if (upScrollX < mMaxLength / 2) {//否則隱藏
                        deltaX = -upScrollX;
                        mDeleteBtnState = 1;
                    }
                }

                //item自動滑動到指定位置
                mScroller.startScroll(upScrollX, 0, deltaX, 0, 200);
                isStartScroll = true;
                invalidate();
                float v = Math.abs(Math.abs(upX) - Math.abs(mDownX));
                float v1 = Math.abs(Math.abs(upY) - Math.abs(mDownY));
                Log.d(TAG, "onTouchEvent:v= "+upX+" "+mDownX+" "+v1);
                mVelocityTracker.clear();
                if (Math.abs(getScrollYDistance() - scrollY) <= 0 && !isItemMoving&&v1<=v) {
                    mOnListItemClickListener.onListItemClick(mPosition);
                }
                isItemMoving = false;
                int scrollYDistance = getScrollYDistance();
                Log.d(TAG, "onTouchEvent:ydistance "+scrollYDistance);
                break;
        }
        Log.d(TAG, "onTouchEvent: "+x);
        mLastX = x;
        mLastY = y;// 
        return super.onTouchEvent(e);
    }

    @Override
    public void computeScroll() {
        Log.d(TAG, "computeScroll: ");
        if (mScroller.computeScrollOffset()) {
            mItemLayout.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        } else if (isStartScroll) {
            isStartScroll = false;
            if (mDeleteBtnState == 1) {
                mDeleteBtnState = 0;
            }

            if (mDeleteBtnState == 2) {
                mDeleteBtnState = 3;
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        mVelocityTracker.recycle();
        super.onDetachedFromWindow();
    }

    public void setOnItemDeleteListener(OnItemDeleteListener listener) {
        mListener = listener;
    }

    public void setOnListItemClickListener(OnListItemClickListener listener) {
        this.mOnListItemClickListener = listener;
    }

    /**
     * 列表點擊
     */
    public interface OnListItemClickListener {
        void onListItemClick(int position);
    }

    public interface OnItemDeleteListener {
        /**
         * 刪除按鈕回調
         * @param position
         */
        void onDeleteClick(int position);
    }

    public int getScrollYDistance() {
        LinearLayoutManager layoutManager = (LinearLayoutManager) this.getLayoutManager();
        int position = layoutManager.findFirstVisibleItemPosition();
        View view = layoutManager.findViewByPosition(position);
        int itemHeight = view.getHeight();
        Log.d("TAG", "getScrollYDistance: "+position+"  "+view.getTop()+" "+view.getLeft()+" "+view.getRight()+" "+view.getBottom()+" "+view.getPaddingBottom()+" "+view.getPaddingLeft());
        return (position) * itemHeight - view.getTop();//view.getTop()表示的當前view相對於父控件的座標有正負
    }

}

條目的xml文件代碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">

    <LinearLayout
        android:id="@+id/layout_alarm_root"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:orientation="vertical">

            <TextView
                android:id="@+id/txt_alarm_event"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="開會"
                android:textColor="#600000"
                android:textSize="21sp"/>


            <ImageView
                android:id="@+id/img_alarm_switch"
                android:layout_width="40dp"
                android:layout_height="22dp"
                android:layout_alignParentEnd="true"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:src="@mipmap/ic_launcher"/>

        </RelativeLayout>

        <TextView
            android:id="@+id/txt_alarm_delete"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:layout_marginStart="5dp"
            android:background="#88990000"
            android:gravity="center"
            android:text="刪除"
            android:textColor="#99ff0000"/>
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_alignParentBottom="true"
        android:layout_below="@+id/txt_alarm_day"
        android:background="#33979797"/>
</LinearLayout>

activity的xml文件代碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.hitv.recyclerview.MainActivity">
<com.hitv.recyclerview.SwipeRecyclerview
    android:layout_weight="10"
    android:id="@+id/sp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

</com.hitv.recyclerview.SwipeRecyclerview>


</LinearLayout>

貼出完整代碼的demo下載地址:http://download.csdn.net/download/today_work/10207552
最後如果測試有什麼問題,請各位指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章