Android 左滑刪除控件

背景:在android開發(fā)中,列表是經(jīng)常會(huì)使用到的一個(gè)主要控件涩禀,列表中可以展示大量的數(shù)據(jù)料滥,像訂單、商品艾船、通訊錄葵腹、瀏覽記錄或者關(guān)注列表等等∮炱瘢可能產(chǎn)品一開始需求只做簡單的數(shù)據(jù)展示践宴,但后期隨著功能越來越多,越來越完善爷怀,產(chǎn)品可能說在列表里面增加一些交互能力阻肩。比如說訂單列表里面,一開始只是展示訂單數(shù)據(jù)运授,后面需要加上刪除訂單的功能烤惊,以前Android中這種功能要的很多的可能就是長按操作這種的乔煞,因?yàn)槌绦蛟持恍枰苌俚拇a就能實(shí)現(xiàn)。但是ios的習(xí)慣操作是左滑刪除撕氧,為了保持統(tǒng)一的操作習(xí)慣瘤缩,兩端保持一致,最終產(chǎn)品會(huì)讓Android程序猿去實(shí)現(xiàn)一種和ios一模一樣的功能伦泥。如果你的代碼已經(jīng)維護(hù)了很久剥啤,代碼量比較大,不愿意去大改不脯,那么今天這個(gè)控件就能輕松的助你完成左滑刪除的功能府怯。

先上效果圖:

效果圖.gif

設(shè)計(jì)思路:最好以最小的代碼侵入來實(shí)現(xiàn)左滑刪除的功能,在不破壞原來邏輯的基礎(chǔ)上防楷,只需稍加改造便可具備左滑刪除的能力牺丙。
首先分析下左滑刪除的基礎(chǔ)原理:

左滑刪除.png

原理分析:

  1. 正常狀態(tài)下,我們看到的是完整的內(nèi)容部分复局,右側(cè)菜單部分因?yàn)槌銎聊凰圆辉谝暰€范圍內(nèi)冲簿。
  2. 手指滑動(dòng)過程中,容器的內(nèi)容跟隨手指移動(dòng)亿昏,從而拉出在屏幕外面的菜單區(qū)域峦剔。
  3. 當(dāng)手指松開的時(shí)候,我們先假定一種邏輯角钩,如果菜單區(qū)域顯示超過一半吝沫,那就全部顯示;如果少于一半那就滑出隱藏递礼。

滑動(dòng)原理分析完了之后惨险,我們大概就有了實(shí)現(xiàn)思路了:

  1. 首先我們的控件里面需要兩塊區(qū)域,因?yàn)橐郧翱赡芤呀?jīng)實(shí)現(xiàn)了列表item的顯示脊髓,如果能不做任何改動(dòng)辫愉,直接把以前的item包含到我們的內(nèi)容區(qū)域里面來,那么我們內(nèi)容區(qū)域就輕松搞定了将硝。
  2. 菜單區(qū)域恭朗,需要什么能力,就把相關(guān)的View也傳遞給我容器袋哼,然后容器放到相應(yīng)位置冀墨。

談笑間闸衫,簡單兩步我們的左滑刪除容器已經(jīng)完成一個(gè)簡單的雛形了涛贯!

接下來就是代碼實(shí)現(xiàn):

步驟一:內(nèi)容和菜單分別加入容器

/**
     * 設(shè)置內(nèi)容區(qū)域
     * @param contentView
     */
    public void addContentView(View contentView) {
        this.mContentView = contentView;
        this.mContentView.setTag("contentView");
        
        View cv = findViewWithTag("contentView");
        if (cv != null) {
            this.removeView(cv);
        }
        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );
        this.addView(this.mContentView, layoutParams);
    }
    
    /**
     * 設(shè)置右邊菜單區(qū)域
     */
    public void addMenuView(View menuView) {
        this.mMenuView = menuView;
        this.mMenuView.setTag("menuView");
        
        View mv = findViewWithTag("menuView");
        if (mv != null) {
            this.removeView(mv);
        }
        LayoutParams layoutParams = new LayoutParams(mRightCanSlide, ViewGroup.LayoutParams.MATCH_PARENT);
        this.addView(this.mMenuView, layoutParams);
    }

步驟二:左滑處理

/**
     * 攔截觸摸事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        
        int actionMasked = ev.getActionMasked();
        
        Log.e(TAG, "onInterceptTouchEvent: actionMasked = " + actionMasked);
        
        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN:
                mInitX = ev.getRawX() + getScrollX();
                mInitY = ev.getRawY();
                clearAnim();
                
                if (mViewPager != null) {
                    mViewPager.requestDisallowInterceptTouchEvent(true);
                }
                
                if (mCardView != null) {
                    mCardView.requestDisallowInterceptTouchEvent(true);
                }
                
                break;
            
            case MotionEvent.ACTION_MOVE:
                
                if (mInitX - ev.getRawX() < 0) {
                    
                    // 讓父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(false);
                        isReCompute = false;
                    }
                    
                    // 阻止ViewPager攔截事件
                    if (mViewPager != null) {
                        mViewPager.requestDisallowInterceptTouchEvent(true);
                    }
                    
                    return false;
                }
                
                // y軸方向上達(dá)到滑動(dòng)最小距離, x 軸未達(dá)到
                if (Math.abs(ev.getRawY() - mInitY) >= mTouchSlop
                        && Math.abs(ev.getRawY() - mInitY) > Math.abs(mInitX - ev.getRawX() - getScrollX())) {
                    
                    // 讓父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(false);
                        isReCompute = false;
                    }
                    
                    return false;
                    
                }
                
                // x軸方向達(dá)到了最小滑動(dòng)距離,y軸未達(dá)到
                if (Math.abs(mInitX - ev.getRawX() - getScrollX()) >= mTouchSlop
                        && Math.abs(ev.getRawY() - mInitY) <= Math.abs(mInitX - ev.getRawX() - getScrollX())) {
                    
                    // 阻止父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(true);
                        isReCompute = false;
                    }
                    
                    return true;
                }
                
                break;
            
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                
                if (mRecyclerView != null) {
                    mRecyclerView.requestDisallowInterceptTouchEvent(false);
                    isReCompute = true;
                }
                break;
            default:
                break;
        }
        
        return super.onInterceptTouchEvent(ev);
    }
/**
     * 處理觸摸事件
     * 需要注意何時(shí)處理左滑蔚出,何時(shí)不處理
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        
        int actionMasked = ev.getActionMasked();
        
        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN:
                mInitX = ev.getRawX() + getScrollX();
                mInitY = ev.getRawY();
                clearAnim();
                
                if (mViewPager != null) {
                    mViewPager.requestDisallowInterceptTouchEvent(true);
                }
                
                if (mCardView != null) {
                    mCardView.requestDisallowInterceptTouchEvent(true);
                }
                
                break;
            
            case MotionEvent.ACTION_MOVE:
                
                if (mInitX - ev.getRawX() < 0) {
                    
                    // 讓父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(false);
                        isReCompute = false;
                    }
                    
                    // 阻止ViewPager攔截事件
                    if (mViewPager != null) {
                        mViewPager.requestDisallowInterceptTouchEvent(true);
                        isReCompute = false;
                    }
                }
                
                // y軸方向上達(dá)到滑動(dòng)最小距離, x 軸未達(dá)到
                if (Math.abs(ev.getRawY() - mInitY) >= mTouchSlop
                        && Math.abs(ev.getRawY() - mInitY) > Math.abs(mInitX - ev.getRawX() - getScrollX())) {
                    
                    // 讓父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(false);
                        isReCompute = false;
                    }
                }
                
                // x軸方向達(dá)到了最小滑動(dòng)距離弟翘,y軸未達(dá)到
                if (Math.abs(mInitX - ev.getRawX() - getScrollX()) >= mTouchSlop
                        && Math.abs(ev.getRawY() - mInitY) <= Math.abs(mInitX - ev.getRawX() - getScrollX())) {
                    
                    // 阻止父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(true);
                        isReCompute = false;
                    }
                }
                
                
                /** 如果手指移動(dòng)距離超過最小距離 */
                float translationX = mInitX - ev.getRawX();
                
                // 如果滑動(dòng)距離已經(jīng)大于右邊可伸縮的距離后, 應(yīng)該重新設(shè)置initx
                if (translationX > mRightCanSlide) {
                    mInitX = ev.getRawX() + mRightCanSlide;
                    
                }
                
                // 如果互動(dòng)距離小于0虫腋,那么重新設(shè)置初始位置initx
                if (translationX < 0) {
                    mInitX = ev.getRawX();
                }
                
                translationX = translationX > mRightCanSlide ? mRightCanSlide : translationX;
                translationX = translationX < 0 ? 0 : translationX;
                
                // 向左滑動(dòng)
                if (translationX <= mRightCanSlide && translationX >= 0) {
                    
                    scrollTo((int) translationX, 0);
                    
                    return true;
                }
                
                break;
            
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                
                if (mRecyclerView != null) {
                    mRecyclerView.requestDisallowInterceptTouchEvent(false);
                    isReCompute = true;
                }
                
                upAnim();
                
                return true;
                
                default:
                    break;
        }
        
        return true;
    }

以上兩個(gè)方法主要處理了左滑移動(dòng)功能以及滑動(dòng)沖突問題,如果用的是RecyclerView那么為了防止垂直方向的同向沖突稀余,那么需要將外層的RecyclerView傳入左滑容器悦冀,在這個(gè)容器中會(huì)處理滑動(dòng)沖突。

到這就已經(jīng)實(shí)現(xiàn)了左滑功能睛琳,并且解決掉了垂直方向上的滑動(dòng)沖突盒蟆,然后我們還要實(shí)現(xiàn)一個(gè)功能是:如果有一個(gè)item向左滑動(dòng)并顯示出右邊的菜單區(qū)域,當(dāng)手指再次按下或者列表滑動(dòng)的時(shí)候师骗,需要將已經(jīng)顯示菜單區(qū)域的item收起历等,恢復(fù)原來的狀態(tài)。為了提供這個(gè)能力辟癌,左滑容器里面提供一個(gè)菜單狀態(tài)變化的監(jiān)聽:

/**
     * 刪除按鈕狀態(tài)變化監(jiān)聽
     */
    public interface OnDelViewStatusChangeLister {
        
        /**
         * 狀態(tài)變化監(jiān)聽
         * @param show  是否正在顯示
         */
        void onStatusChange(boolean show);
    }


/**
     * 重置 菜單展開/菜單收起 狀態(tài)
     */
    public void resetDelStatus() {
        
        int scrollX = getScrollX();
        
        if (scrollX == 0) {
            return;
        }
        
        clearAnim();
        
        mValueAnimator = ValueAnimator.ofInt(scrollX, 0);
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                
                scrollTo(value, 0);
            }
        });
        
        mValueAnimator.setDuration(mAnimDuring);
        mValueAnimator.start();
    }

菜單展開或者收起都會(huì)調(diào)用這個(gè)方法寒屯,方便第三方調(diào)用者處理狀態(tài)。

再者還有就是加上動(dòng)畫黍少,讓滑動(dòng)更加柔和:

/**
     * 手指抬起執(zhí)行動(dòng)畫
     */
    private void upAnim() {
        int scrollX = getScrollX();
        
        if (scrollX == mRightCanSlide || scrollX == 0) {
            
            if (mStatusChangeLister != null) {
                mStatusChangeLister.onStatusChange(scrollX == mRightCanSlide);
            }
            
            return;
        }
        
        clearAnim();
        
        // 如果顯出一半松開手指寡夹,那么自動(dòng)完全顯示。否則完全隱藏
        if (scrollX >= mRightCanSlide / 2) {
            mValueAnimator = ValueAnimator.ofInt(scrollX, mRightCanSlide);
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int value = (int) animation.getAnimatedValue();
                    
                    scrollTo(value, 0);
                }
            });
            
            mValueAnimator.setDuration(mAnimDuring);
            mValueAnimator.start();
            
            if (mStatusChangeLister != null) {
                mStatusChangeLister.onStatusChange(true);
            }
        }
        else {
            mValueAnimator = ValueAnimator.ofInt(scrollX, 0);
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int value = (int) animation.getAnimatedValue();
                    
                    scrollTo(value, 0);
                }
            });
            
            mValueAnimator.setDuration(mAnimDuring);
            mValueAnimator.start();
            
            if (mStatusChangeLister != null) {
                mStatusChangeLister.onStatusChange(false);
            }
        }
    }

最后貼上左滑刪除容器的完整代碼:

/**
 * @author luowang
 * @date 2020-08-19 17:31
 * 左滑刪除View
 */
public class LeftSlideView extends LinearLayout {
    
    /**
     * tag
     */
    public static final String TAG = "LeftSlideView";
    
    /**
     * 上下文
     */
    private Context mContext;
    
    
    /**
     * 最小觸摸距離
     */
    private int mTouchSlop;
    
    
    /**
     * 右邊可滑動(dòng)距離
     */
    private int mRightCanSlide;
    
    
    /**
     * 按下x
     */
    private float mInitX;
    
    /**
     * 按下y
     */
    private float mInitY;
    
    
    /**
     * 屬性動(dòng)畫
     */
    private ValueAnimator mValueAnimator;
    
    
    /**
     * 動(dòng)畫時(shí)長
     */
    private int mAnimDuring = 200;
    
    /**
     * 刪除按鈕的長度
     */
    private int mDelLength = 76;
    
    /**
     * ViewPager
     */
    private ViewPager mViewPager;
    
    /**
     * RecyclerView
     */
    private RecyclerView mRecyclerView;
    
    /** CardView */
    private CardView mCardView;
    
    /** 是否重新計(jì)算 */
    private boolean isReCompute = true;
    
    
    /** 狀態(tài)監(jiān)聽 */
    private OnDelViewStatusChangeLister mStatusChangeLister;
    
    /**
     * 內(nèi)容區(qū)域View
     */
    private View mContentView;
    
    /**
     * 菜單區(qū)域View
     */
    private View mMenuView;
    
    
    
    public LeftSlideView(Context context) {
        this(context, null);
    }
    
    public LeftSlideView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public LeftSlideView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        
        init();
    }
    
    
    /**
     * 初始化
     */
    private void init() {
        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
        mRightCanSlide = DPIUtil.dip2px(mContext, mDelLength);
        setBackgroundColor(Color.TRANSPARENT);
        // 水平布局
        setOrientation(LinearLayout.HORIZONTAL);
        initView();
    }
    
    /**
     * 設(shè)置內(nèi)容區(qū)域
     * @param contentView
     */
    public void addContentView(View contentView) {
        this.mContentView = contentView;
        this.mContentView.setTag("contentView");
        
        View cv = findViewWithTag("contentView");
        if (cv != null) {
            this.removeView(cv);
        }
        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );
        this.addView(this.mContentView, layoutParams);
    }
    
    /**
     * 設(shè)置右邊菜單區(qū)域
     */
    public void addMenuView(View menuView) {
        this.mMenuView = menuView;
        this.mMenuView.setTag("menuView");
        
        View mv = findViewWithTag("menuView");
        if (mv != null) {
            this.removeView(mv);
        }
        LayoutParams layoutParams = new LayoutParams(mRightCanSlide, ViewGroup.LayoutParams.MATCH_PARENT);
        this.addView(this.mMenuView, layoutParams);
    }
    
    
    /**
     * 設(shè)置Viewpager
     */
    public void setViewPager(ViewPager viewPager) {
        mViewPager = viewPager;
    }
    
    /**
     * 設(shè)置RecyclerView
     */
    public void setRecyclerView(RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
    }
    
    /** 設(shè)置CardView */
    public void setCardView(CardView cardView) {
        mCardView = cardView;
    }
    
    /** 設(shè)置狀態(tài)監(jiān)聽 */
    public void setStatusChangeLister(OnDelViewStatusChangeLister statusChangeLister) {
        mStatusChangeLister = statusChangeLister;
    }
    
    /**
     * 初始化View
     */
    private void initView() {
        
    
    
    }
    
    
    /**
     * 攔截觸摸事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        
        int actionMasked = ev.getActionMasked();
        
        Log.e(TAG, "onInterceptTouchEvent: actionMasked = " + actionMasked);
        
        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN:
                mInitX = ev.getRawX() + getScrollX();
                mInitY = ev.getRawY();
                clearAnim();
                
                if (mViewPager != null) {
                    mViewPager.requestDisallowInterceptTouchEvent(true);
                }
                
                if (mCardView != null) {
                    mCardView.requestDisallowInterceptTouchEvent(true);
                }
                
                break;
            
            case MotionEvent.ACTION_MOVE:
                
                if (mInitX - ev.getRawX() < 0) {
                    
                    // 讓父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(false);
                        isReCompute = false;
                    }
                    
                    // 阻止ViewPager攔截事件
                    if (mViewPager != null) {
                        mViewPager.requestDisallowInterceptTouchEvent(true);
                    }
                    
                    return false;
                }
                
                // y軸方向上達(dá)到滑動(dòng)最小距離, x 軸未達(dá)到
                if (Math.abs(ev.getRawY() - mInitY) >= mTouchSlop
                        && Math.abs(ev.getRawY() - mInitY) > Math.abs(mInitX - ev.getRawX() - getScrollX())) {
                    
                    // 讓父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(false);
                        isReCompute = false;
                    }
                    
                    return false;
                    
                }
                
                // x軸方向達(dá)到了最小滑動(dòng)距離厂置,y軸未達(dá)到
                if (Math.abs(mInitX - ev.getRawX() - getScrollX()) >= mTouchSlop
                        && Math.abs(ev.getRawY() - mInitY) <= Math.abs(mInitX - ev.getRawX() - getScrollX())) {
                    
                    // 阻止父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(true);
                        isReCompute = false;
                    }
                    
                    return true;
                }
                
                break;
            
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                
                if (mRecyclerView != null) {
                    mRecyclerView.requestDisallowInterceptTouchEvent(false);
                    isReCompute = true;
                }
                break;
            default:
                break;
        }
        
        return super.onInterceptTouchEvent(ev);
    }
    
    /**
     * 處理觸摸事件
     * 需要注意何時(shí)處理左滑菩掏,何時(shí)不處理
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        
        int actionMasked = ev.getActionMasked();
        
        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN:
                mInitX = ev.getRawX() + getScrollX();
                mInitY = ev.getRawY();
                clearAnim();
                
                if (mViewPager != null) {
                    mViewPager.requestDisallowInterceptTouchEvent(true);
                }
                
                if (mCardView != null) {
                    mCardView.requestDisallowInterceptTouchEvent(true);
                }
                
                break;
            
            case MotionEvent.ACTION_MOVE:
                
                if (mInitX - ev.getRawX() < 0) {
                    
                    // 讓父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(false);
                        isReCompute = false;
                    }
                    
                    // 阻止ViewPager攔截事件
                    if (mViewPager != null) {
                        mViewPager.requestDisallowInterceptTouchEvent(true);
                        isReCompute = false;
                    }
                }
                
                // y軸方向上達(dá)到滑動(dòng)最小距離, x 軸未達(dá)到
                if (Math.abs(ev.getRawY() - mInitY) >= mTouchSlop
                        && Math.abs(ev.getRawY() - mInitY) > Math.abs(mInitX - ev.getRawX() - getScrollX())) {
                    
                    // 讓父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(false);
                        isReCompute = false;
                    }
                }
                
                // x軸方向達(dá)到了最小滑動(dòng)距離,y軸未達(dá)到
                if (Math.abs(mInitX - ev.getRawX() - getScrollX()) >= mTouchSlop
                        && Math.abs(ev.getRawY() - mInitY) <= Math.abs(mInitX - ev.getRawX() - getScrollX())) {
                    
                    // 阻止父級(jí)容器攔截
                    if (mRecyclerView != null && isReCompute) {
                        mRecyclerView.requestDisallowInterceptTouchEvent(true);
                        isReCompute = false;
                    }
                }
                
                
                /** 如果手指移動(dòng)距離超過最小距離 */
                float translationX = mInitX - ev.getRawX();
                
                // 如果滑動(dòng)距離已經(jīng)大于右邊可伸縮的距離后, 應(yīng)該重新設(shè)置initx
                if (translationX > mRightCanSlide) {
                    mInitX = ev.getRawX() + mRightCanSlide;
                    
                }
                
                // 如果互動(dòng)距離小于0农渊,那么重新設(shè)置初始位置initx
                if (translationX < 0) {
                    mInitX = ev.getRawX();
                }
                
                translationX = translationX > mRightCanSlide ? mRightCanSlide : translationX;
                translationX = translationX < 0 ? 0 : translationX;
                
                // 向左滑動(dòng)
                if (translationX <= mRightCanSlide && translationX >= 0) {
                    
                    scrollTo((int) translationX, 0);
                    
                    return true;
                }
                
                break;
            
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                
                if (mRecyclerView != null) {
                    mRecyclerView.requestDisallowInterceptTouchEvent(false);
                    isReCompute = true;
                }
                
                upAnim();
                
                return true;
                
                default:
                    break;
        }
        
        return true;
    }
    
    
    /**
     * 清除動(dòng)畫
     */
    private void clearAnim() {
        if (mValueAnimator == null) {
            return;
        }
        
        mValueAnimator.end();
        mValueAnimator.cancel();
        mValueAnimator = null;
    }
    
    
    /**
     * 手指抬起執(zhí)行動(dòng)畫
     */
    private void upAnim() {
        int scrollX = getScrollX();
        
        if (scrollX == mRightCanSlide || scrollX == 0) {
            
            if (mStatusChangeLister != null) {
                mStatusChangeLister.onStatusChange(scrollX == mRightCanSlide);
            }
            
            return;
        }
        
        clearAnim();
        
        // 如果顯出一半松開手指患蹂,那么自動(dòng)完全顯示。否則完全隱藏
        if (scrollX >= mRightCanSlide / 2) {
            mValueAnimator = ValueAnimator.ofInt(scrollX, mRightCanSlide);
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int value = (int) animation.getAnimatedValue();
                    
                    scrollTo(value, 0);
                }
            });
            
            mValueAnimator.setDuration(mAnimDuring);
            mValueAnimator.start();
            
            if (mStatusChangeLister != null) {
                mStatusChangeLister.onStatusChange(true);
            }
        }
        else {
            mValueAnimator = ValueAnimator.ofInt(scrollX, 0);
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int value = (int) animation.getAnimatedValue();
                    
                    scrollTo(value, 0);
                }
            });
            
            mValueAnimator.setDuration(mAnimDuring);
            mValueAnimator.start();
            
            if (mStatusChangeLister != null) {
                mStatusChangeLister.onStatusChange(false);
            }
        }
    }
    
    /**
     * 重置
     */
    public void resetDelStatus() {
        
        int scrollX = getScrollX();
        
        if (scrollX == 0) {
            return;
        }
        
        clearAnim();
        
        mValueAnimator = ValueAnimator.ofInt(scrollX, 0);
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                
                scrollTo(value, 0);
            }
        });
        
        mValueAnimator.setDuration(mAnimDuring);
        mValueAnimator.start();
    }
    
    /**
     * 刪除按鈕狀態(tài)變化監(jiān)聽
     */
    public interface OnDelViewStatusChangeLister {
        
        /**
         * 狀態(tài)變化監(jiān)聽
         * @param show  是否正在顯示
         */
        void onStatusChange(boolean show);
    }
    
}

完整DEMO直通車:https://github.com/wwluo14/LeftSlideEdit

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末砸紊,一起剝皮案震驚了整個(gè)濱河市传于,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌醉顽,老刑警劉巖沼溜,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異游添,居然都是意外死亡系草,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門唆涝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來找都,“玉大人,你說我怎么就攤上這事廊酣∧艹埽” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晓猛。 經(jīng)常有香客問我饿幅,道長,這世上最難降的妖魔是什么戒职? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任栗恩,我火速辦了婚禮,結(jié)果婚禮上洪燥,老公的妹妹穿的比我還像新娘磕秤。我一直安慰自己,他們只是感情好捧韵,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布亲澡。 她就那樣靜靜地躺著,像睡著了一般纫版。 火紅的嫁衣襯著肌膚如雪床绪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天其弊,我揣著相機(jī)與錄音癞己,去河邊找鬼。 笑死梭伐,一個(gè)胖子當(dāng)著我的面吹牛痹雅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播糊识,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼绩社,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了赂苗?” 一聲冷哼從身側(cè)響起愉耙,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拌滋,沒想到半個(gè)月后朴沿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡败砂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年赌渣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昌犹。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坚芜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斜姥,到底是詐尸還是另有隱情鸿竖,我是刑警寧澤路操,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站千贯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏搞坝。R本人自食惡果不足惜搔谴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望桩撮。 院中可真熱鬧敦第,春花似錦、人聲如沸店量。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽融师。三九已至右钾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旱爆,已是汗流浹背舀射。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留怀伦,地道東北人脆烟。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像房待,于是被迫代替她去往敵國和親邢羔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359