文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2022-06-06 11:47

关注

效果演示

需求和技术分析

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


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">
  <android.support.v7.widget.RecyclerView
    android:id="@+id/rv_main"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
</LinearLayout>

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

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


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="?android:listPreferredItemHeight"
  android:background="?selectableItemBackground">
  <ImageView
    android:id="@+id/iv_touch"
    style="@style/ItemStyle"
    android:layout_height="match_parent"
    android:layout_alignParentEnd="true"
    android:layout_alignParentRight="true"
    android:src="@android:drawable/alert_dark_frame" />
  <CheckBox
    android:id="@+id/cb_item_check"
    style="@style/ItemStyle"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true" />
  <TextView
    android:id="@+id/tv_name"
    style="@style/ItemStyle"
    android:layout_toEndOf="@id/cb_item_check"
    android:layout_toRightOf="@id/cb_item_check" />
  <TextView
    android:id="@+id/tv_sex"
    style="@style/ItemStyle"
    android:layout_marginLeft="@dimen/dp_10"
    android:layout_marginStart="@dimen/dp_10"
    android:layout_toEndOf="@id/tv_name"
    android:layout_toRightOf="@id/tv_name" />
</RelativeLayout>

这个也不用解释了,到时候下载看源码,就是普通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 {
  
  private OnItemTouchCallbackListener onItemTouchCallbackListener;
  
  private boolean isCanDrag = false;
  
  private boolean isCanSwipe = false;
  public DefaultItemTouchHelpCallback(OnItemTouchCallbackListener onItemTouchCallbackListener) {
    this.onItemTouchCallbackListener = onItemTouchCallbackListener;
  }
  
  public void setOnItemTouchCallbackListener(OnItemTouchCallbackListener onItemTouchCallbackListener) {
    this.onItemTouchCallbackListener = onItemTouchCallbackListener;
  }
  
  public void setDragEnable(boolean canDrag) {
    isCanDrag = canDrag;
  }
  
  public void setSwipeEnable(boolean canSwipe) {
    isCanSwipe = canSwipe;
  }
  
  @Override
  public boolean isLongPressDragEnabled() {
    return isCanDrag;
  }
  
  @Override
  public boolean isItemViewSwipeEnabled() {
    return isCanSwipe;
  }
  
  @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;
  }
  
  @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 {
    
    void onSwiped(int adapterPosition);
    
    boolean onMove(int srcPosition, int targetPosition);
  }
}

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



@Override
public boolean isLongPressDragEnabled() {}

@Override
public boolean isItemViewSwipeEnabled() {}

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {}

@Override
public boolean onMove(RecyclerView r, ViewHolder rholer, ViewHolder tholder) {}

@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();
  }
  
  public void setDragEnable(boolean canDrag) {
    itemTouchHelpCallback.setDragEnable(canDrag);
  }
  
  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;
  }
};

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

您可能感兴趣的文章:Android RecyclerView实现点击条目删除Android自定义SwipeLayout仿QQ侧滑条目Android仿京东分类模块左侧分类条目效果Android更多条目收缩展开控件ExpandView的示例代码Android ListView自动生成列表条目的实例Android XRecyclerView实现多条目加载Android条目拖拽删除功能实例代码Android ListView 条目多样式展示实例详解Android中listview和imageview实现条目单选效果Android编程实现canvas绘制饼状统计图功能示例【自动适应条目数量与大小】Android中RecyclerView上拉下拉,分割线,多条目的实例代码Android 中 SwipeLayout一个展示条目底层菜单的侧滑控件源码解析详解Android中实现ListView左右滑动删除条目的方法Android实现下拉展示条目效果


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯