RecyclerView 扩展(四) - 使用ItemTouchHelper实现QQ侧滑删除效果

  咱们平时在QQ过程当中,有一个效果是咱们不可忽略的,那就是消息记录的侧滑删除功能,我相信这个功能不少人都用过吧。不得不说,QQ的交互确实作得很是优秀。今天,咱们模仿一下QQ的侧滑效果(这自己是楼主毕设里面的一个小效果,我只是将它分离出来),首先来看看Demo的效果: git

  今天,咱们就来看看上面效果怎么来实现。在阅读本文以前,我默认你们已经掌握了 ItemTouchHelper,若是还有同窗没有学过,能够参考个人文章:

  1. RecyclerView 扩展(二) - 手把手教你认识ItemTouchHelper
  2. RecyclerView 扩展(三) - 使用ItemTouchHelper和LayoutManager实现滑动卡片效果

1. 概述

  在正式讲解如上的效果是怎么实现以前,咱们来看看上面效果都有那几个问题须要考虑的:github

  1. 怎么实现左右侧滑?ItemTouchHelper?ItemTouchHelper怎么实现侧滑以后不删除,而是停留在某种状态上。
  2. 从上面的效果图中,咱们能够明确的感觉到左右滑动均可以超出RecyclerView的边界,只是在手指松开以后,ItemView会回到正确的位置而已。像这种overscroll效果应该怎么实现?
  3. 当停留在删除状态时,此时ItemView还能够从左右滑动(QQ好像只能向右滑动😂)。这种效果又应该怎么实现呢?

  针对上面的三个问题,本文会详细的分析是怎么实现的。 #2. 再来了解ItemTouchHelper.Callback   咱们知道,ItemTouchHelper的核心在于ItemTouchHelper.Callback接口中,因此在正式分析实现以前,咱们先来了解temTouchHelper.Callback的几个方法。bash

方法名 做用
getSwipeThreshold 当 侧滑滑动的距离 / RecyclerView的宽大于该方法返回值,那么就会触发侧滑删除的操做。具体是:此时ItemView会作位移动画,当ItemView不可见时,会触发ItemTouchHelperonSwiped方法,进而咱们在onSwiped方法里面对Adapter进行remove操做。
getSwipeEscapeVelocity 当侧滑的速度大于该方法的返回值,也会触发侧滑删除的操做。
onChildDraw 此方法是本文的核心方法。该方法在ItemView进行滑动时会回调,这里的滑动包括:1.手指滑动;2.ItemView的位移动画。能够根据isCurrentlyActive参数来判断是手指滑动仍是动画滑动。
clearView 此方法是本文的核心方法。该方法在ItemView滑动完成以后会回调,因此想要实现侧滑ItemView停在某种状态,此方法是核心之点。

  通过对上面几个方法的理解,如今,我来简单描述一下效果的实现方法:   首先咱们将滑动距离的阈值和滑动速度的阈值设置到最大,保证不会触发本来的侧滑删除操做。   当手指滑动时,咱们就让它开心的滑动,不去怎么作任何的操做;当手指松开时,由于速度阈值和距离阈值都是最大,因此不可能超过它,那么ItemView会作动画回到最初的状态,可是咱们不能按照原来的规则来位移ItemView,因此咱们得判断手指松开那时ItemView的滑动距离是否大于咱们设置的阈值(是否显示删除按钮的阈值),若是大于的话,那么就作动画到给定的位置就行,若是小于的话,就回到原始位置。ide

3. 实现

  如今咱们正式来看一下代码,首先看一下getSwipeThreshold方法和getSwipeEscapeVelocity方法:动画

@Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return Integer.MAX_VALUE;
    }

    @Override
    public float getSwipeEscapeVelocity(float defaultValue) {
        return Integer.MAX_VALUE;
    }
复制代码

  我这里均设置为Integer.MAX_VALUE。其实getSwipeThreshold不用设置那么大,设置为1.1应该就好了,毕竟是百分比。   如今咱们来看看onChildDraw方法的实现:ui

@Override
    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        // 首次滑动时,记录下ItemView当前滑动的距离
        if (dX == 0) {
            mCurrentScrollX = viewHolder.itemView.getScrollX();
            mFirstInactive = true;
        }
        if (isCurrentlyActive) { // 手指滑动
            // 基于当前的距离滑动
            viewHolder.itemView.scrollTo(mCurrentScrollX + (int) -dX, 0);
        } else { // 动画滑动
            if (mFirstInactive) {
                mFirstInactive = false;
                mCurrentScrollXWhenInactive = viewHolder.itemView.getScrollX();
                mInitXWhenInactive = dX;
            }
            if (viewHolder.itemView.getScrollX() >= mDefaultScrollX) {
                // 当手指松开时,ItemView的滑动距离大于给定阈值,那么最终就停留在阈值,显示删除按钮。
                viewHolder.itemView.scrollTo(Math.max(mCurrentScrollX + (int) -dX, mDefaultScrollX), 0);
            } else {
                // 这里只能作距离的比例缩放,由于回到最初位置必须得从当前位置开始,dx不必定与ItemView的滑动距离相等
                viewHolder.itemView.scrollTo((int) (mCurrentScrollXWhenInactive * dX / mInitXWhenInactive), 0);
            }
        }
    }
复制代码

  须要注意的是,为了不滑动时出现跳变,因此咱们在手动时,必须基于当前的滑动距离来滑动,由于每一次手指滑动开始,dx都是从0开始的,这样就表示每一次手指滑动ItemView都从最初的位置开始滑动。   还须要注意一点,就是当手指中止滑动时,咱们须要判断ItemView最终应该处于那种状态。这里说的状态无非是两种,一种是删除状态,一种最初状态。判断了状态以后,咱们须要将ItemView位移到指定位置,这里两种方式:spa

  1. 本身实现动画,来滑动到指定位置。
  2. 经过onChildDraw方法的回调来滑动到指定位置。

  这里我使用的是第二种方式,相对来讲,第一种方式比较简单,毕竟是本身实现动画;而第二种方式须要dx的值。具体的细节你们能够看我代码上的注释。code

  咱们再来看clearView方法的实现:cdn

@Override
    public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        if (viewHolder.itemView.getScrollX() > mDefaultScrollX) {
            viewHolder.itemView.scrollTo(mDefaultScrollX, 0);
        } else if (viewHolder.itemView.getScrollX() < 0) {
            viewHolder.itemView.scrollTo(0, 0);
        }
        mItemTouchStatus.onSaveItemStatus(viewHolder);
    }
复制代码

  clearView方法的做用就是当ItemView滑动完成以后,进行一些处理,这里咱们必须保证ItemView滑动到指定位置,由于在计算过程当中不免会有一点点偏差,因此要进行归位处理。接口

4. github

  为了方便你们的理解,我将个人代码上传到github:SlideDeleteDemo

相关文章
相关标签/搜索