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

相關文章
相關標籤/搜索