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

androidRecyclerView实现条目Item拖拽排序与滑动删除

效果演示 需求和技术分析 RecyclerView Item拖拽排序::长按RecyclerView的Item或者触摸Ite

效果演示

需求和技术分析

  • RecyclerView Item拖拽排序::长按RecyclerView的Item或者触摸Item的某个按钮。
  • RecyclerView Item滑动删除:RecyclerView Item滑动删除:RecyclerView的Item滑动删除。

实现方案与技术

利用ItemTouchHelper绑定RecyclerView、ItemTouchHelper.Callback来实现UI更新,并且实现动态控制是否开启拖拽功能和滑动删除功能。

实现步骤

  • 继承抽象类ItemTouchHelper,并在构造方法传入实现的ItemTouchHelper.Callback。
  • recyclerView绑定ItemTouchHelper:itemTouchHelper.attachToRecyclerView(recyclerView)。
  • 自定义ItemTouchHelper.Callback的实现接口OnItemTouchCallbackListener,由外部更新RecyclerView的Item。

几个主要的布局

activity_main.xml

<&#63;xml version="1.0" encoding="utf-8"&#63;>


  



这个没啥好说的了吧,就是一个RecyclerView啦。

接下来是RecyclerView的Item的布局item.xml:

<&#63;xml version="1.0" encoding="utf-8"&#63;>


  

  

  

  



这个也不用解释了,到时候下载看源码,就是普通item,展示数据而已。

实现自己的DefaultItemTouchHelper:继承ItemTouchHelper

public class DefaultItemTouchHelper extends ItemTouchHelper {
  public DefaultItemTouchHelper(ItemTouchHelp.Callback callback) {
    super(callback);
  }
}

好嘛,这个太简单了,基本上一行代码都不用写。但是这里需要一个ItemTouchHelp.Callback啊,所以我们还是要实现一个ItemTouchHelp.Callback,客观且看下文分解。

实现自己的ItemTouchHelper.Callback:继承ItemTouchHelper.Callback

这里是全文最重要的部分啦,要认真点看噢,先上代码,后解释,其他看注释和视频。

public class DefaultItemTouchHelpCallback extends ItemTouchHelper.Callback {

  /**
   * Item操作的回调
   */
  private OnItemTouchCallbackListener onItemTouchCallbackListener;

  /**
   * 是否可以拖拽
   */
  private boolean isCanDrag = false;
  /**
   * 是否可以被滑动
   */
  private boolean isCanSwipe = false;

  public DefaultItemTouchHelpCallback(OnItemTouchCallbackListener onItemTouchCallbackListener) {
    this.OnItemTouchCallbackListener= onItemTouchCallbackListener;
  }

  /**
   * 设置Item操作的回调,去更新UI和数据源
   *
   * @param onItemTouchCallbackListener
   */
  public void setOnItemTouchCallbackListener(OnItemTouchCallbackListener onItemTouchCallbackListener) {
    this.OnItemTouchCallbackListener= onItemTouchCallbackListener;
  }

  /**
   * 设置是否可以被拖拽
   *
   * @param canDrag 是true,否false
   */
  public void setDragEnable(boolean canDrag) {
    isCanDrag = canDrag;
  }

  /**
   * 设置是否可以被滑动
   *
   * @param canSwipe 是true,否false
   */
  public void setSwipeEnable(boolean canSwipe) {
    isCanSwipe = canSwipe;
  }

  /**
   * 当Item被长按的时候是否可以被拖拽
   *
   * @return
   */
  @Override
  public boolean isLongPressDragEnabled() {
    return isCanDrag;
  }

  /**
   * Item是否可以被滑动(H:左右滑动,V:上下滑动)
   *
   * @return
   */
  @Override
  public boolean isItemViewSwipeEnabled() {
    return isCanSwipe;
  }

  /**
   * 当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向
   *
   * @param recyclerView
   * @param viewHolder
   * @return
   */
  @Override
  public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    if (layoutManager instanceof GridLayoutManager) {// GridLayoutManager
      // flag如果值是0,相当于这个功能被关闭
      int dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN;
      int swipeFlag = 0;
      // create make
      return makeMovementFlags(dragFlag, swipeFlag);
    } else if (layoutManager instanceof LinearLayoutManager) {// linearLayoutManager
      LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
      int orientation = linearLayoutManager.getOrientation();

      int dragFlag = 0;
      int swipeFlag = 0;

      // 为了方便理解,相当于分为横着的ListView和竖着的ListView
      if (orientation == LinearLayoutManager.HORIZONTAL) {// 如果是横向的布局
        swipeFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
      } else if (orientation == LinearLayoutManager.VERTICAL) {// 如果是竖向的布局,相当于ListView
        dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
      }
      return makeMovementFlags(dragFlag, swipeFlag);
    }
    return 0;
  }

  /**
   * 当Item被拖拽的时候被回调
   *
   * @param recyclerView   recyclerView
   * @param srcViewHolder  拖拽的ViewHolder
   * @param targetViewHolder 目的地的viewHolder
   * @return
   */
  @Override
  public boolean onMove(RecyclerView recyclerView, ViewHolder srcViewHolder, ViewHolder targetViewHolder) {
    if (onItemTouchCallbackListener != null) {
      return onItemTouchCallbackListener.onMove(srcViewHolder.getAdapterPosition(), targetViewHolder.getAdapterPosition());
    }
    return false;
  }

  @Override
  public void onSwiped(ViewHolder viewHolder, int direction) {
    if (onItemTouchCallbackListener != null) {
      onItemTouchCallbackListener.onSwiped(viewHolder.getAdapterPosition());
    }
  }

  public interface OnItemTouchCallbackListener {
    /**
     * 当某个Item被滑动删除的时候
     *
     * @param adapterPosition item的position
     */
    void onSwiped(int adapterPosition);

    /**
     * 当两个Item位置互换的时候被回调
     *
     * @param srcPosition  拖拽的item的position
     * @param targetPosition 目的地的Item的position
     * @return 开发者处理了操作应该返回true,开发者没有处理就返回false
     */
    boolean onMove(int srcPosition, int targetPosition);
  }
}

好,其实上面最重要的就是五个方法:

/**
 * 是否可以长按拖拽排序。
 */
@Override
public boolean isLongPressDragEnabled() {}
/**
 * Item是否可以被滑动(H:左右滑动,V:上下滑动)
 */
@Override
public boolean isItemViewSwipeEnabled() {}
/**
 * 当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向
 */
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {}
/**
 * 当Item被拖拽的时候被回调
 */
@Override
public boolean onMove(RecyclerView r, ViewHolder rholer, ViewHolder tholder) {}

/**
 * 当View被滑动删除的时候
 */
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {}



isItemViewSwipeEnabled()返回值是否可以拖拽排序,true可以,false不可以,isItemViewSwipeEnabled()是否可以滑动删除,true可以,false不可以;这两个方法都是配置是否可以操作的。我们上面的代码中返回了一个成员变量值,并且这个值通过外部可以修改,所以提供了外部控制的方法。

onMove()当Item被拖拽排序移动到另一个Item的位置的时候被回调,onSwiped()当Item被滑动删除到不见;这两个方法是当用户操作了,来回调我们,我们就该去更新UI了。这里我们提供了一个Listener去通知外部,并且返回出去了必要的值,来降低代码耦合度。

getMovementFlags()说明一:是当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向,那我们又知道支持拖拽和滑动删除的无非就是LinearLayoutManager和GridLayoutManager了,相当于我们老早的时候用的ListView和GridView了。所以我们根据布局管理器的不同做了响应的区分。

getMovementFlags()说明二:其他都好理解,就是这里的return makeMovementFlags(dragFlag, swipeFlag);这句话是最终的返回值,也就是它决定了我们的拖拽或者滑动的方法。第一个参数是拖拽flag,第二个是滑动的flag。

重新定义DefaultItemTouchHelper

我们记得上面定义了一个DefaultItemTouchHelper,它的构造中需要传一个ItemTouchHelper.Callback,既然我们实现礼了,我们再把DefaultItemTouchHelper做个封装,使使用者更傻瓜式的调用。

public class DefaultItemTouchHelper extends YolandaItemTouchHelper {

  private DefaultItemTouchHelpCallback itemTouchHelpCallback;

  public DefaultItemTouchHelper(DefaultItemTouchHelpCallback.OnItemTouchCallbackListener onItemTouchCallbackListener) {
    super(new DefaultItemTouchHelpCallback(onItemTouchCallbackListener));
    itemTouchHelpCallback = (DefaultItemTouchHelpCallback) getCallback();
  }

  /**
   * 设置是否可以被拖拽
   *
   * @param canDrag 是true,否false
   */
  public void setDragEnable(boolean canDrag) {
    itemTouchHelpCallback.setDragEnable(canDrag);
  }

  /**
   * 设置是否可以被滑动
   *
   * @param canSwipe 是true,否false
   */
  public void setSwipeEnable(boolean canSwipe) {
    itemTouchHelpCallback.setSwipeEnable(canSwipe);
  }
}

现在我们看到已经不需要传ItemTouchHelper.Callback给ItemTouchHelper了,只需要传我们在DefaultItemTouchHelpCallback中定义好的OnItemTouchCallbackListener就好了,而且提供了设置是否可以滑动和是否可以拖拽的方法,而OnItemTouchCallbackListener只是通知外部滑动了、删除了,你去更新UI吧。

这里可以有的同学会有疑问,上面原来不是继承ItemTouchHelper吗?这里咋就变成了YolandaItemTouchHelper了呢?因为我们看到这里多了一句itemTouchHelpCallback = getCallback();,这个getCallback();这个方法是没有的,是我们在YolandaItemTouchHelper中自定义的,因为我们想在DefaultItemTouchHelper中提供外部设置是否可以拖拽和滑动删除的方法,就得拿到这个Callback,所以我看了下源码,我们在ItemTouchHelper构造中把Callback穿进去,它保存的时候一个package级别的成员变量,所以我在Android.support.v7.widget.helper包下新建了一个YolandaItemTouchHelper类:

public class YolandaItemTouchHelper extends ItemTouchHelper {
  public YolandaItemTouchHelper(Callback callback) {
    super(callback);
  }

  public Callback getCallback() {
    return mCallback;
  }
}

如何投入使用

好扯淡也扯完了,封装也封装完了,那么接下来就来在Activity中使用下咯:

recyclerView绑定ItemTouchHelper

没啥好说的用itemTouchHelper.attachToRecyclerView(recyclerView)绑定recyclerView和ItemTouchHelper,并且只是允许拖拽和滑动删除Item:

DefaultItemTouchHelper itemTouchHelper = new DefaultItemTouchHelper(onItemTouchCallbackListener);
itemTouchHelper.attachToRecyclerView(recyclerView);
itemTouchHelper.setDragEnable(true);
itemTouchHelper.setSwipeEnable(true);

看到上面还缺少一个onItemTouchCallbackListener吧,这个也比较重要。

使用Callback自定义的OnItemTouchCallbackListener刷新UI

我们在自定义Callback的时候不是在onMove()和onSwiped()方法中回调OnItemTouchCallbackListener去更新UI吗?这里就是OnItemTouchCallbackListener如何更新UI的操作了,完成这个操作,那么我们的目的就达到了:

private DefaultItemTouchHelpCallback.OnItemTouchCallbackListener OnItemTouchCallbackListener= new DefaultItemTouchHelpCallback.OnItemTouchCallbackListener() {
  @Override
  public void onSwiped(int adapterPosition) {
    // 滑动删除的时候,从数据源移除,并刷新这个Item。
    if (userInfoList != null) {
      userInfoList.remove(adapterPosition);
      mainAdapter.notifyItemRemoved(adapterPosition);
    }
  }

  @Override
  public boolean onMove(int srcPosition, int targetPosition) {
    if (userInfoList != null) {
      // 更换数据源中的数据Item的位置
      Collections.swap(userInfoList, srcPosition, targetPosition);
      // 更新UI中的Item的位置,主要是给用户看到交互效果
      mainAdapter.notifyItemMoved(srcPosition, targetPosition);
      return true;
    }
    return false;
  }
};

到这里就结束了,不信你去试试,源码传送门。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 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
手机用户2502936007
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有