热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

这篇文章主要使用了RecyclerView的ItemTouchHelper类实现了Item的拖动和删除功能,ItemTouchHelper是v7包下的一个类,下面来看看详细的介绍吧,需要的朋友可以参考学习。

前言

从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

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

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

源码下载:点击这里 

总结

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


推荐阅读
  • 2023 ARM嵌入式系统全国技术巡讲旨在分享ARM公司在半导体知识产权(IP)领域的最新进展。作为全球领先的IP提供商,ARM在嵌入式处理器市场占据主导地位,其产品广泛应用于90%以上的嵌入式设备中。此次巡讲将邀请来自ARM、飞思卡尔以及华清远见教育集团的行业专家,共同探讨当前嵌入式系统的前沿技术和应用。 ... [详细]
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
  • 理解存储器的层次结构有助于程序员优化程序性能,通过合理安排数据在不同层级的存储位置,提升CPU的数据访问速度。本文详细探讨了静态随机访问存储器(SRAM)和动态随机访问存储器(DRAM)的工作原理及其应用场景,并介绍了存储器模块中的数据存取过程及局部性原理。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
author-avatar
my76572
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有