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)

需要注意的是,如果我们想实现拖动或者滑动必须将上面是否支持拖动或者滑动的方法返回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);
 }
}

在默认情况下是支持拖动和滑动的,也就是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);
 }

我们在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);
  }
 }

经过上面的处理,发现此时滑动可以看到我们定制的蓝低的删除背景了,此时可能还有疑问,这样删除是不是很生硬,那就再给它加一个透明度的动画,如下。

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

源码下载:点击这里

总结

好了,到这里RecyclerView实现滑动删除和拖拽功能的已经介绍完毕了。希望本文的内容对各位Android开发者们能带来一定的帮助,如有问题欢迎留言指出。

(0)

相关推荐

  • Android 滑动监听RecyclerView线性流+左右划删除+上下移动

    废话不多说了,直接给大家贴代码了.具体代码如下所示: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_wid

  • Android RecyclerView添加上拉加载更多效果

    先看一下效果 刷新的时候是这样的: 没有更多的时候是这样的: 既然有刷新的时候有两种状态就要定义两个状态 //普通布局的type static final int TYPE_ITEM = 0; //脚布局 static final int TYPE_FOOTER = 1; 在特定的时候去显示: @Override public int getItemViewType(int position) { //如果position加1正好等于所有item的总和,说明是最后一个item,将它设置为脚布局

  • Android 给RecyclerView添加分割线的具体步骤(分享)

    [吐槽]RecyclerView没有提供分割线的方法,想要加个线还要自己画,点击事件的监听都要自己实现,不过真的好用. 给RecyclerView添加分割线的步骤 1.新建类继承于RecyclerView.ItemDecoration,此为是抽象类: public static abstract class ItemDecoration { public void onDraw(Canvas c, RecyclerView parent, State state) { onDraw(c, par

  • Android中RecyclerView 滑动时图片加载的优化

    RecyclerView 滑动时的优化处理,在滑动时停止加载图片,在滑动停止时开始加载图片,这里用了Glide.pause 和Glide.resume.这里为了避免重复设置增加开销,设置了一个标志变量来做判断. mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, in

  • android给RecyclerView加上折叠的效果示例

    RecyclerView有很高的自由度,可以说只有想不到没有做不到,真是越用越喜欢.这次用超简单的方法,让RecyclerView带上折叠的效果. 效果是这样的. 总结一下这个列表的特点,就是以下三点: 1. 重叠效果: 2. 层次感: 3. 首项的差动效果. 下面我们来一个个解决. 我们新建一个ParallaxRecyclerView,让它继承RecyclerView,并使用LinearLayoutManager作为布局管理器. 重叠效果 其实就是每一项都搭一部分在它前面那项而已.我们知道,R

  • Android recyclerview实现拖拽排序和侧滑删除

    Recyclerview现在基本已经替代Listview了,RecyclerView也越来越好用了  当我们有实现条目的拖拽排序和侧滑删除时  可以直接时候Recyclerview提供的API就可以直接实现了 先贴上主要代码 private void initveiw() { ArrayList<String> items = new ArrayList<>(Arrays.asList("itme1", "item2", "itme

  • Android 中RecyclerView通用适配器的实现

    Android 中RecyclerView通用适配器的实现 前言: SDK的5.0版本出来已经N久了,可以说是已经经过许多人的检验了,里面的新控件不能说是非常完美,但也是非常好用了,其中最让我喜爱的就是RecyclerView了,可以完美替代ListView和GridView(除了添加headerview和footview了,网上有许多解决方式.这个下面会以一种简单的方式顺带解决,肯定为大家省心),而且可以代码动态切换这两种布局方式以及瀑布流布局.相关切换方式网上有很多,大家自行搜索,我就不贴连

  • Android RecyclerView的卡顿问题的解决方法

    RecyclerView为什么会卡 RecyclerView作为v7包的新控件,自从推出就广受Android Developer们欢迎,实际上它已经取代了ListView和GridView两位老前辈的地位.然而不少亲们想必也已经发现了:没有优化过的Recycler性能很poor.上一篇博主使用的item也仅仅是一个图两串字而已,结果一滑动就卡的要命,不能忍! 那么why?回想在用ListView和GridView的adapter时,我们是用一种叫ViewHolder的自定义类(容器)来实现优化的

  • 浅谈Android为RecyclerView增加监听以及数据混乱的小坑

    为 RecyclerView增加监听 1.在实现好的MyAdapter中写内部接口: public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) { this.onItemLongClickListener = onItemLongClickListener; } public void setOnItemClickListener(OnItemClickListener onIt

  • Android中RecyclerView实现滑动删除与拖拽功能

    前言 从Android 5.0开始,谷歌推出了新的控件RecyclerView,相对于早它之前的ListView,优点多多,功能强大,也给我们的开发着提供了极大的便利,今天自己学习一下RecyclerView轻松实现滑动删除及拖拽的效果. 如下图. 相信研究过RecyclerView的同学,应该很清楚该怎么实现这样的效果,若是用ListView,这样的效果实现起来可能就有点麻烦,但是在强大的RecyclerView面前这样的的效果只需很少的代码,因为谷歌给我们提供了强大的工具类ItemTouch

  • Android使用ItemTouchHelper实现侧滑删除和拖拽

    本文实例为大家分享了如何使用ItemTouchHelper实现侧滑删除和拖拽的具体代码,供大家参考,具体内容如下 1. 定义一个简单bean类: public class ImgText { public int resId; public String des; } 2. 实现一个RecyclerView.Adapter public class SwipeRecyclerAdapter extends RecyclerView.Adapter<SwipeRecyclerAdapter.MyH

  • Android中RecyclerView嵌套滑动冲突解决的代码片段

    在纵向RecyclerView嵌套横向RecyclerView时,如果纵向RecyclerView有下拉刷新功能,那么内部的横向RecyclerView的横向滑动体验会很差.(只有纯横向滑动时,才能滑动内部的横向RecyclerView,否则滑动事件就会影响到下拉刷新),添加拦截判断. public class MySwipeRefreshLayout extends SwipeRefreshLayout { private boolean mIsVpDragger; private final

  • Android中recyclerView底部添加透明渐变效果

    前言 最近实现一个recyclerView透明渐变的效果,遇到了一些坑,尝试了一些方法,这里记录一下. 效果图 图片在上面显示2列,文字在下面显示1列:底部要有个透明渐变的效果,直到完全看不到. gridLayoutManager动态设置列数 大概是分两类,一类以图片为item 一行2个,一类以文字为item 一行一个. 这个第一反应是用viewType去区分图片类型,但是由于起初不知道gridLayout可以动态列数.就在上面两列,下面一列上为难起来了. 如果统一用一列吧,那就把两个image

  • vue3 elmentPlus table实现列宽可拖拽功能

    el-table 里边的border属性,设置之后表格即可直接拖拽,下文内容是相关扩展功能的实现 最近公司项目里边需求要让表格的宽度可拖动,我们的公司的项目有vue2的也有vue3的,表格分别使用了element UI和element Plus,前者的社区比较丰富,我们使用了mizuka-wu/el-table-draggable 但是对于后者,我查到社区相关的插件并不多,但也找到了guolaopi/element-plus-table-dragable-demo 把demo下载下来之后,我发现

  • Android使用CardView作为RecyclerView的Item并实现拖拽和左滑删除

    引言 CardView是Android 5.0系统之后引入的众多控件之一,实现之后的效果也是比较酷的,它经常被用在RecyclerView和ListView中的Item中.今天我们就来了解一下CardView的属性,然后使用CardView和RecyclerView结合实现一个可以拖拽Item的布局. CardView的属性 CardView继承自FrameLayout,所以子控件的布局规则和FrameLayout的一样,是按照层次堆叠的 下面是CardView的一些常用属性: CardView

  • Android中RecyclerView拖拽、侧删功能的实现代码

    废话不多说,下面展示一下效果. 这是GridView主文件实现. public class GridViewActivity extends AppCompatActivity { RecyclerView mRecyclerView; List<String> mStringList; RecyclerAdapter mRecyAdapter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { s

  • Android中RecyclerView实现Item添加和删除的代码示例

    本文介绍了Android中RecyclerView实现Item添加和删除的代码示例,分享给大家,具体如下: 先上效果图: RecyclerView简介: RecyclerView用以下两种方式简化了数据的展示和处理: 1. 使用LayoutManager来确定每一个item的排列方式. 2. 为增加和删除项目提供默认的动画效果,也可以自定义. RecyclerView项目结构如下: Adapter:使用RecyclerView之前,你需要一个继承自RecyclerView.Adapter的适配器

  • Android中RecyclerView实现横向滑动代码

    RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍Android中RecyclerView实现横向滑动代码,一起看看吧. android.support.v7.widget.RecyclerView 功能:RecyclerView横向滑动 控件:<android.support.v7.widget.RecyclerView /> Java类:RecyclerView.GalleryAdap

  • Android RecyclerView实现滑动删除

    本文实例为大家分享了RecyclerView实现滑动删除的具体代码,供大家参考,具体内容如下 package com.example.demo; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.Lin

随机推荐