RecyclerView實現滑動刪除和拖拽功能

AshleyTavar 7年前發布 | 8K 次閱讀 Android開發 移動開發 RecyclerView

前言

從Android 5.0開始,谷歌推出了新的控件RecyclerView,相對于早它之前的ListView,優點多多,功能強大,也給我們的開發著提供了極大的便利,今天自己學習一下RecyclerView輕松實現滑動刪除及拖拽的效果,如下圖。

這里寫圖片描述

相信研究過RecyclerView的同學,應該很清楚該怎么實現這樣的效果,若是用ListView,這樣的效果實現起來可能就有點麻煩,但是在強大的RecyclerView面前這樣的的效果只需很少的代碼,因為谷歌給我們提供了強大的工具類ItemTouchHelper,它已經處理了關于RecyclerView拖動和滑動的實現,并且我們可以在其中實現我們自己的動畫,以及定制我們想要的效果。

ItemTouchHelper.Callback

ItemTouchHelper.Callback有幾個重要的抽象方法,我們繼承該抽象類,并重寫抽象方法。它是我們實現滑動和拖拽重要的回調。

int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)

該方法返回一個整數,用來指定拖拽和滑動在哪個方向是被允許的。在其中使用makeMovementFlags(int dragFlags, int swipeFlags)返回,該方法第一個參數用來指定拖動,第二個參數用來指定滑動。對于方向參數有6種

ItemTouchHelper.UP  //滑動拖拽向上方向
ItemTouchHelper.DOWN//向下
ItemTouchHelper.LEFT//向左
ItemTouchHelper.RIGHT//向右
ItemTouchHelper.START//依賴布局方向的水平開始方向
ItemTouchHelper.END//依賴布局方向的水平結束方向
boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)

onMove方法是拖拽的回調,參數viewHolder是拖動的Item,target是拖動的目標位置的Item,該方法如果返回true表示切換了位置,反之返回false。

void onSwiped(RecyclerView.ViewHolder viewHolder, int direction)

onSwiped方法為Item滑動回調,viewHolder為滑動的item,direction為滑動的方向。

上面三個方法是必須重寫的方法,當然還有其它一些可供選擇的方法。

/**

 * Item是否支持長按拖動
 *
 * @return
 *          true  支持長按操作
 *          false 不支持長按操作
 */

boolean isLongPressDragEnabled()

/**
 * Item是否支持滑動
 *
 * @return
 *          true  支持滑動操作
 *          false 不支持滑動操作
 */

boolean isItemViewSwipeEnabled()

/**
 * 移動過程中繪制Item
 *
 * @param c
 * @param recyclerView
 * @param viewHolder
 * @param dX
 *          X軸移動的距離
 * @param dY
 *          Y軸移動的距離
 * @param actionState
 *          當前Item的狀態
 * @param isCurrentlyActive
 *          如果當前被用戶操作為true,反之為false
 */

onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive)</code></pre>

需要注意的是,如果我們想實現拖動或者滑動必須將上面是否支持拖動或者滑動的方法返回true,否則onMove或者onSwiped方法不會執行。

功能實現

adapter=new CustomAdapter(getActivity(),strings);
        recycleview.setAdapter(adapter);
        ItemTouchHelper.Callback callback=new RecycleItemTouchHelper(adapter);
        ItemTouchHelper itemTouchHelper=new ItemTouchHelper(callback);
        itemTouchHelper.attachToRecyclerView(recycleview);

對于ItemTouchHelper 構造方法接收一個ItemTouchHelper.Callback參數,而這個Callback就是我們在在上述講到的工具類,初始化ItemTouchHelper 后通過其attachToRecyclerView(@Nullable RecyclerView recyclerView)方法將我們實現的ItemTouchHelper.Callback和RecyclerView關聯,最終達到我們的效果,代碼看起來是不是很簡單,接下來我們看下我們自定義的Callback。

package com.example.xh.adapter;

import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.View;

import com.example.xh.R; import com.example.xh.utils.MyApplication;

/**

  • Created by xiehui on 2017/2/23. */ public class RecycleItemTouchHelper extends ItemTouchHelper.Callback{ private static final String TAG ="RecycleItemTouchHelper" ; private final ItemTouchHelperCallback helperCallback;

    public RecycleItemTouchHelper(ItemTouchHelperCallback helperCallback) {

     this.helperCallback = helperCallback;
    

    }

    /**

    • 設置滑動類型標記 *
    • @param recyclerView
    • @param viewHolder
    • @return
    • 返回一個整數類型的標識,用于判斷Item那種移動行為是允許的 */ @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { Log.e(TAG, "getMovementFlags: " ); //START 右向左 END左向右 LEFT 向左 RIGHT向右 UP向上 //如果某個值傳0,表示不觸發該操作,次數設置支持上下拖拽,支持向右滑動 return makeMovementFlags(ItemTouchHelper.UP|ItemTouchHelper.DOWN,ItemTouchHelper.END ); } /**
    • Item是否支持長按拖動 *
    • @return
    • true 支持長按操作
    • false 不支持長按操作 */ @Override public boolean isLongPressDragEnabled() { return super.isLongPressDragEnabled(); } /**
    • Item是否支持滑動 *
    • @return
    • true 支持滑動操作
    • false 不支持滑動操作 */ @Override public boolean isItemViewSwipeEnabled() { return super.isItemViewSwipeEnabled(); } /**
    • 拖拽切換Item的回調 *
    • @param recyclerView
    • @param viewHolder
    • @param target
    • @return
    • 如果Item切換了位置,返回true;反之,返回false */ @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { Log.e(TAG, "onMove: " ); helperCallback.onMove(viewHolder.getAdapterPosition(),target.getAdapterPosition()); return true; } /**
    • 滑動Item *
    • @param viewHolder
    • @param direction
    • Item滑動的方向 */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { Log.e(TAG, "onSwiped: "); helperCallback.onItemDelete(viewHolder.getAdapterPosition()); } /**
    • Item被選中時候回調 *
    • @param viewHolder
    • @param actionState
    • 當前Item的狀態
    • ItemTouchHelper.ACTION_STATE_IDLE 閑置狀態
    • ItemTouchHelper.ACTION_STATE_SWIPE 滑動中狀態
    • ItemTouchHelper#ACTION_STATE_DRAG 拖拽中狀態 */ @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { super.onSelectedChanged(viewHolder, actionState); } public interface ItemTouchHelperCallback{ void onItemDelete(int positon); void onMove(int fromPosition,int toPosition); } }</code></pre>

      在默認情況下是支持拖動和滑動的,也就是isLongPressDragEnabled()和isItemViewSwipeEnabled()是返回true的。在該類中我們創建了一個接口ItemTouchHelperCallback并創建兩個抽象方法,分別表示拖拽和滑動。在onMove方法中回調創建我們創建的接口方法接口onMove(int fromPosition,int toPosition),并將拖拽和 Item 的posion和目標posion傳入,posion通過ViewHolder的getAdapterPosition()獲得,然后在滑動回調方法onSwiped中回調onItemDelete(int positon)。到這里我們自定義的ItemTouchHelper.Callback創建完成。

      上面完成后我們只需要在我們自定義的Adapter中實現RecycleItemTouchHelper.ItemTouchHelperCallback接口,然后在回調方法中更新界面,如下Apdater中回調方法實現。

      @Override
      public void onItemDelete(int positon) {
      list.remove(positon);
      notifyItemRemoved(positon);
      }

      @Override public void onMove(int fromPosition, int toPosition) { Collections.swap(list,fromPosition,toPosition);//交換數據 notifyItemMoved(fromPosition,toPosition); }</code></pre>

      我們在onItemDelete方法中刪除對應posion的數據,在onMove方法中通過Collections.swap方法交換對應項數據,然后分別調用notifyItemRemoved和notifyItemMoved通過適配器更新UI.

      好了到這里功能已經實現了,是不是發現代碼很少,當然啰嗦的比較多而已。

      功能升級

      通過上面簡單代碼的實現,已經可以滑動刪除和拖拽了,當然不滿足的你可能發現滑動刪除的時候沒有動畫沒有背景,但是我想更改下背景并且在滑動的過程會出現一個刪除的圖標,給用戶反饋,讓其明白該操作是刪除數據的。當然你還會想再刪除的過程中增加一個動畫。其實實現這個效果也并不是很麻煩,接下來新的方法實現登場。哦,不對,前面提到過的。

      onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive)

      該方法就是移動過程中繪制Item的回調。我們在actionState==ItemTouchHelper.ACTION_STATE_SWIPE時,即為滑動的時候繪制背景和刪除圖片。

      初始化

      /**

    • 移動過程中繪制Item *
    • @param c
    • @param recyclerView
    • @param viewHolder
    • @param dX
    • X軸移動的距離
    • @param dY
    • Y軸移動的距離
    • @param actionState
    • 當前Item的狀態
    • @param isCurrentlyActive
    • 如果當前被用戶操作為true,反之為false */ @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { //滑動時自己實現背景及圖片 if (actionState==ItemTouchHelper.ACTION_STATE_SWIPE){

      //dX大于0時向右滑動,小于0向左滑動 View itemView=viewHolder.itemView;//獲取滑動的view Resources resources= MyApplication.getAppContext().getResources(); Bitmap bitmap= BitmapFactory.decodeResource(resources, R.drawable.delete);//獲取刪除指示的背景圖片 int padding =10;//圖片繪制的padding int maxDrawWidth=2*padding+bitmap.getWidth();//最大的繪制寬度 Paint paint=new Paint(); paint.setColor(resources.getColor(R.color.btninvalid)); int x=Math.round(Math.abs(dX)); int drawWidth=Math.min(x,maxDrawWidth);//實際的繪制寬度,取實時滑動距離x和最大繪制距離maxDrawWidth最小值 int itemTop=itemView.getBottom()-itemView.getHeight();//繪制的top位置 //向右滑動 if(dX>0){ //根據滑動實時繪制一個背景 c.drawRect(itemView.getLeft(),itemTop,drawWidth,itemView.getBottom(),paint); //在背景上面繪制圖片 if (x>padding){//滑動距離大于padding時開始繪制圖片

      //指定圖片繪制的位置
      Rect rect=new Rect();//畫圖的位置
      rect.left=itemView.getLeft()+padding;
      rect.top=itemTop+(itemView.getBottom()-itemTop-bitmap.getHeight())/2;//圖片居中
      int maxRight=rect.left+bitmap.getWidth();
      rect.right=Math.min(x,maxRight);
      rect.bottom=rect.top+bitmap.getHeight();
      //指定圖片的繪制區域
      Rect rect1=null;
      if (x<maxRight){
          rect1=new Rect();//不能再外面初始化,否則dx大于畫圖區域時,刪除圖片不顯示
          rect1.left=0;
          rect1.top = 0;
          rect1.bottom=bitmap.getHeight();
          rect1.right=x-padding;
      }
      c.drawBitmap(bitmap,rect1,rect,paint);
      

      } //繪制時需調用平移動畫,否則滑動看不到反饋 itemView.setTranslationX(dX); }else { //如果在getMovementFlags指定了向左滑動(ItemTouchHelper。START)時則繪制工作可參考向右的滑動繪制,也可直接使用下面語句交友系統自己處理 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }else { //拖動時有系統自己完成 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }</code></pre>

      經過上面的處理,發現此時滑動可以看到我們定制的藍低的刪除背景了,此時可能還有疑問,這樣刪除是不是很生硬,那就再給它加一個透明度的動畫,如下。

      float alpha = 1.0f - Math.abs(dX) / (float) itemView.getWidth();
      itemView.setAlpha(alpha);

      好了,到這里RecyclerView實現滑動刪除和拖拽功能的已經介紹完畢了。有問題歡迎留言指出,Have a wonderful day .

       

      來自:http://www.jianshu.com/p/2ae483118c8e

       

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