热门标签 | 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;
  }
};

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

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


推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
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社区 版权所有