自定義RecyclerView 支持 橫向縱向,滑動刪除Item

jopen 9年前發布 | 192K 次閱讀 Android開發 移動開發 RecyclerView

項目源碼已經放上去:https://github.com/xufeifandj/SwipeRecyclerView


本項目直接 繼承于RecyclerView 本身就支持 橫向縱向的 列表。

具體代碼請看代碼。

    package com.ferris.holeswipeview;

import android.content.Context;  
import android.support.v7.widget.RecyclerView;  
import android.util.AttributeSet;  
import android.view.MotionEvent;  
import android.view.VelocityTracker;  
import android.view.View;  
import android.view.ViewConfiguration;  
import android.view.WindowManager;  
import android.widget.AdapterView;  
import android.widget.Scroller;  

/** 
 * http://blog.csdn.net/xufeifandj www.github.com/xufeifandj 
 *  
 * @ferris 459821731@qq.com 
 *  
 */  
public class CusomSwipeView extends RecyclerView {  
    private Orientation orientation = Orientation.HORIZONTAL;  
    /** 
     * 當前滑動的ListView position 
     */  
    private int slidePosition;  
    /** 
     * 手指按下X的坐標 
     */  
    private int downY;  
    /** 
     * 手指按下Y的坐標 
     */  
    private int downX;  
    /** 
     * 屏幕寬度 
     */  
    private int screenWidth;  
    /** 
     * ListView的item 
     */  
    private View itemView;  
    /** 
     * 滑動類 
     */  
    private Scroller scroller;  
    private static final int SNAP_VELOCITY = 600;  
    /** 
     * 速度追蹤對象 
     */  
    private VelocityTracker velocityTracker;  
    /** 
     * 是否響應滑動,默認為不響應 
     */  
    private boolean isSlide = false;  
    /** 
     * 認為是用戶滑動的最小距離 
     */  
    private int mTouchSlop;  
    /** 
     * 移除item后的回調接口 
     */  
    private RemoveListener mRemoveListener;  
    /** 
     * 用來指示item滑出屏幕的方向,向左或者向右,用一個枚舉值來標記 
     */  
    private RemoveDirection removeDirection;  

    // 滑動刪除方向的枚舉值  
    public enum RemoveDirection {  
        RIGHT, LEFT;  
    }  

    public CusomSwipeView(Context context) {  
        super(context);  
        // TODO Auto-generated constructor stub  
        init(context);  
    }  

    public CusomSwipeView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        // TODO Auto-generated constructor stub  
        init(context);  
    }  

    public CusomSwipeView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
        // TODO Auto-generated constructor stub  
        init(context);  
    }  

    public void init(Context context) {  

        if (orientation == Orientation.VERTICAL) {  
            screenWidth = ((WindowManager) context  
                    .getSystemService(Context.WINDOW_SERVICE))  
                    .getDefaultDisplay().getWidth();  
        } else {  
            screenWidth = ((WindowManager) context  
                    .getSystemService(Context.WINDOW_SERVICE))  
                    .getDefaultDisplay().getHeight();  
        }  
        scroller = new Scroller(context);  
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();  
    }  

    /** 
     * 設置滑動刪除的回調接口 
     *  
     * @param removeListener 
     */  
    public void setRemoveListener(RemoveListener removeListener) {  
        this.mRemoveListener = removeListener;  
    }  

    /** 
     * 分發事件,主要做的是判斷點擊的是那個item, 以及通過postDelayed來設置響應左右滑動事件 
     */  
    @Override  
    public boolean dispatchTouchEvent(MotionEvent event) {  
        switch (event.getAction()) {  
        case MotionEvent.ACTION_DOWN: {  
            addVelocityTracker(event);  

            // 假如scroller滾動還沒有結束,我們直接返回  
            if (!scroller.isFinished()) {  
                return super.dispatchTouchEvent(event);  
            }  
            downX = (int) event.getX();  
            downY = (int) event.getY();  

            itemView = findChildViewUnder(downX, downY);  
            if (itemView == null) {  
                return super.dispatchTouchEvent(event);  
            }  

            slidePosition = getChildPosition(itemView);  
            // 無效的position, 不做任何處理  
            if (slidePosition == AdapterView.INVALID_POSITION) {  
                return super.dispatchTouchEvent(event);  
            }  

            break;  
        }  
        case MotionEvent.ACTION_MOVE: {  
            if (orientation == Orientation.VERTICAL) {// 如果  
                                                        // 左右滑動的距離大于最大的滑動距離,并且沒有上下滑動,就代表  
                                                        // 右滑刪除或者左滑刪除  
                if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY  
                        || (Math.abs(event.getX() - downX) > mTouchSlop && Math  
                                .abs(event.getY() - downY) < mTouchSlop)) {  
                    isSlide = true;  

                }  
            } else {  
                // 如果 上下滑動的距離大于最大的滑動距離,并且沒有左右滑動,就代表上滑刪除或者下滑刪除  
                if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY  
                        || (Math.abs(event.getY() - downY) > mTouchSlop && Math  
                                .abs(event.getX() - downX) < mTouchSlop)) {  
                    isSlide = true;  

                }  

            }  
            break;  
        }  
        case MotionEvent.ACTION_UP:  
            recycleVelocityTracker();  
            break;  
        }  

        return super.dispatchTouchEvent(event);  
    }  

    /** 
     * 往右滑動,getScrollX()返回的是左邊緣的距離,就是以View左邊緣為原點到開始滑動的距離,所以向右邊滑動為負值 
     */  
    private void scrollRight() {  
        if (orientation == Orientation.VERTICAL) {// 往右滑動  
            removeDirection = RemoveDirection.RIGHT;  
            final int delta = (screenWidth + itemView.getScrollX());  
            // 調用startScroll方法來設置一些滾動的參數,我們在computeScroll()方法中調用scrollTo來滾動item  
            scroller.startScroll(itemView.getScrollX(), 0, -delta, 0,  
                    Math.abs(delta));  
            postInvalidate(); // 刷新itemView  
        } else {// 往上滑動  
            removeDirection = RemoveDirection.RIGHT;  
            final int delta = (screenWidth + itemView.getScrollY());  
            // 調用startScroll方法來設置一些滾動的參數,我們在computeScroll()方法中調用scrollTo來滾動item  
            scroller.startScroll(0, itemView.getScrollY(), -delta, 0,  
                    Math.abs(delta));  
            postInvalidate(); // 刷新itemView  
        }  
    }  

    /** 
     * 向左滑動,根據上面我們知道向左滑動為正值 
     */  
    private void scrollLeft() {  
        if (orientation == Orientation.VERTICAL) {// 往左滑動  
            removeDirection = RemoveDirection.LEFT;  
            final int delta = (screenWidth - itemView.getScrollX());  
            // 調用startScroll方法來設置一些滾動的參數,我們在computeScroll()方法中調用scrollTo來滾動item  
            scroller.startScroll(itemView.getScrollX(), 0, delta, 0,  
                    Math.abs(delta));  
            postInvalidate(); // 刷新itemView  
        } else {  
            removeDirection = RemoveDirection.LEFT;  
            final int delta = (screenWidth - itemView.getScrollY());  
            // 調用startScroll方法來設置一些滾動的參數,我們在computeScroll()方法中調用scrollTo來滾動item  
            scroller.startScroll(0, itemView.getScrollY(), delta, 0,  
                    Math.abs(delta));  
            postInvalidate(); // 刷新itemView  
        }  
    }  

    /** 
     * 根據手指滾動itemView的距離來判斷是滾動到開始位置還是向左或者向右滾動 
     */  
    private void scrollByDistanceX() {  
        // 如果向左滾動的距離大于屏幕的二分之一,就讓其刪除  

        if (orientation == Orientation.VERTICAL) {  
            if (itemView.getScrollX() >= screenWidth / 2) {  
                scrollLeft();  
            } else if (itemView.getScrollX() <= -screenWidth / 2) {  
                scrollRight();  
            } else {  
                // 滾回到原始位置,為了偷下懶這里是直接調用scrollTo滾動  
                itemView.scrollTo(0, 0);  
            }  
        } else {  
            if (itemView.getScrollY() >= screenWidth / 2) {  
                scrollLeft();  
            } else if (itemView.getScrollY() <= -screenWidth / 2) {  
                scrollRight();  
            } else {  
                // 滾回到原始位置,為了偷下懶這里是直接調用scrollTo滾動  
                itemView.scrollTo(0, 0);  
            }  
        }  

    }  

    /** 
     * 處理我們拖動ListView item的邏輯 
     */  
    @Override  
    public boolean onTouchEvent(MotionEvent ev) {  
        if (isSlide && slidePosition != AdapterView.INVALID_POSITION  
                && itemView != null) {  
            requestDisallowInterceptTouchEvent(true);  
            addVelocityTracker(ev);  
            final int action = ev.getAction();  
            int x = (int) ev.getX();  
            int y = (int) ev.getY();  
            switch (action) {  
            case MotionEvent.ACTION_DOWN:  
                break;  
            case MotionEvent.ACTION_MOVE:  

                MotionEvent cancelEvent = MotionEvent.obtain(ev);  
                cancelEvent  
                        .setAction(MotionEvent.ACTION_CANCEL  
                                | (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));  
                onTouchEvent(cancelEvent);  

                if (orientation == Orientation.VERTICAL) {  
                    int deltaX = downX - x;  
                    downX = x;  
                    // 手指拖動itemView滾動, deltaX大于0向左滾動,小于0向右滾  
                    itemView.scrollBy(deltaX, 0);  
                } else {  
                    int deltaY = downY - y;  
                    downY = y;  
                    itemView.scrollBy(0, deltaY);  
                }  

                return true; // 拖動的時候ListView不滾動  
            case MotionEvent.ACTION_UP:  
                int velocityX = getScrollVelocity();  
                if (velocityX > SNAP_VELOCITY) {  
                    scrollRight();  
                } else if (velocityX < -SNAP_VELOCITY) {  
                    scrollLeft();  
                } else {  
                    scrollByDistanceX();  
                }  

                recycleVelocityTracker();  
                // 手指離開的時候就不響應左右滾動  
                isSlide = false;  
                break;  
            }  
        }  

        // 否則直接交給ListView來處理onTouchEvent事件  
        return super.onTouchEvent(ev);  
    }  

    @Override  
    public void computeScroll() {  
        // 調用startScroll的時候scroller.computeScrollOffset()返回true,  
        if (scroller.computeScrollOffset()) {  
            // 讓ListView item根據當前的滾動偏移量進行滾動  
            itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());  

            postInvalidate();  

            // 滾動動畫結束的時候調用回調接口  
            if (scroller.isFinished()) {  
                if (mRemoveListener == null) {  
                    throw new NullPointerException(  
                            "RemoveListener is null, we should called setRemoveListener()");  
                }  

                itemView.scrollTo(0, 0);  
                mRemoveListener.removeItem(removeDirection, slidePosition);  
            }  
        }  
    }  

    /** 
     * 添加用戶的速度跟蹤器 
     *  
     * @param event 
     */  
    private void addVelocityTracker(MotionEvent event) {  
        if (velocityTracker == null) {  
            velocityTracker = VelocityTracker.obtain();  
        }  

        velocityTracker.addMovement(event);  
    }  

    /** 
     * 移除用戶速度跟蹤器 
     */  
    private void recycleVelocityTracker() {  
        if (velocityTracker != null) {  
            velocityTracker.recycle();  
            velocityTracker = null;  
        }  
    }  

    /** 
     * 獲取X方向的滑動速度,大于0向右滑動,反之向左 
     *  
     * @return 
     */  
    private int getScrollVelocity() {  
        if (orientation == Orientation.VERTICAL) {  
            velocityTracker.computeCurrentVelocity(1000);  
            int velocity = (int) velocityTracker.getXVelocity();  
            return velocity;  
        }else{  
            velocityTracker.computeCurrentVelocity(1000);  
            int velocity = (int) velocityTracker.getYVelocity();  
            return velocity;  
        }  

    }  

    /** 
     *  
     * 當ListView item滑出屏幕,回調這個接口 我們需要在回調方法removeItem()中移除該Item,然后刷新ListView 
     *  
     *  
     */  
    public interface RemoveListener {  
        public void removeItem(RemoveDirection direction, int position);  
    }  

    public static enum Orientation {  
        HORIZONTAL(0), VERTICAL(1);  

        private int value;  

        private Orientation(int i) {  
            value = i;  
        }  

        public int value() {  
            return value;  
        }  

        public static Orientation valueOf(int i) {  
            switch (i) {  
            case 0:  
                return HORIZONTAL;  
            case 1:  
                return VERTICAL;  
            default:  
                throw new RuntimeException("[0->HORIZONTAL, 1->VERTICAL]");  
            }  
        }  
    }  
}  </pre><br />

來自:http://blog.csdn.net/xufeifandj/article/details/46618423

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!