关闭

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

标签: Item拖拽Item滑动删除侧滑菜单
37210人阅读 评论(40) 收藏 举报
分类:

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

版权声明:转载请注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003


欢迎使用我的另一个更强大的库:RecyclerView侧滑菜单,长按拖拽,滑动删除,下拉刷新上拉加载

效果演示

效果演示

直播视频讲解http://pan.baidu.com/s/1bpHp3I3
推荐大家结合我直播的视频看效果更好。

本博客源码传送门

RecyclerView侧滑菜单
RecyclerView滑动删除
RecyclerView长按拖拽
RecyclerView下拉刷新上拉加载,请看下面这篇博客:
http://blog.csdn.net/yanzhenjie1003/article/details/52115566

需求和技术分析

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

实现方案与技术

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

实现步骤

  1. 继承抽象类ItemTouchHelper,并在构造方法传入实现的ItemTouchHelper.Callback
  2. recyclerView绑定ItemTouchHelper:itemTouchHelper.attachToRecyclerView(recyclerView)
  3. 自定义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 {

    /**
     * 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的时候需要我们告诉系统滑动或者拖拽的方向,那我们又知道支持拖拽和滑动删除的无非就是LinearLayoutManagerGridLayoutManager了,相当于我们老早的时候用的ListViewGridView了。所以我们根据布局管理器的不同做了响应的区分。

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.CallbackItemTouchHelper了,只需要传我们在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)绑定recyclerViewItemTouchHelper,并且只是允许拖拽和滑动删除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;
    }
};

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


版权声明:转载请注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003

35
0
查看评论

RecycleView实现拖拽交换item位置

老规矩,先来一张效果图:相比起ListView而言,RecycleView实现拖拽交换位置的效果要简单很多,因为通过SDK中的ItemTouchHelper工具类可以轻松的实现这种效果,并且一套代码支持所有布局方式;而ListView的话则需要通过生成View的缓存镜像设置到ImageView中,然...
  • mChenys
  • mChenys
  • 2017-02-16 15:42
  • 3566

android开发游记:ItemTouchHelper 使用RecyclerView打造可拖拽的GridView

以下是RecyclerView结合ItemTouchHelper实现的列表和网格布局的拖拽效果。效果图如下:(gif图有点顿卡,其实运行是很流畅的) demo下载地址: DragRecyclerView那么是如何实现的呢?主要就要使用到ItemTouchHelper ,ItemTouchHelpe...
  • liaoinstan
  • liaoinstan
  • 2016-04-20 14:33
  • 21092

RecyclerView拖拽排序和滑动删除实现

效果图如何实现那么是如何实现的呢?主要就要使用到ItemTouchHelper ,ItemTouchHelper 一个帮助开发人员处理拖拽和滑动删除的实现类,它能够让你非常容易实现侧滑删除、拖拽的功能。实现的代码非常简单我们只需要两步:实例化一个ItemTouchHelper 关联到Recycle...
  • whuhan2013
  • whuhan2013
  • 2016-05-19 12:46
  • 1529

ItemTouchHelper 使用RecyclerView打造可拖拽的GridView

以下是RecyclerView结合ItemTouchHelper实现的列表和网格布局的拖拽效果。 效果图如下:(gif图有点顿卡,其实运行是很流畅的) demo下载地址: DragRecyclerView 如何实现 那么是如何实现的呢?主要就要使用到ItemTouchHelper...
  • u014651216
  • u014651216
  • 2016-05-19 20:12
  • 4188

RecyclerView实现支持拖拽、删除、侧滑菜单的列表

什么是RecyclerView 如何使用RecyclerView RecyclerViewAdpater ItemTouchHelper类 关于RecyclerView的一些细节问题 关于RecyclerView的缓存机制 关于RecyclerView中的Position 一些无关的感想  最近项目...
  • Ruidu_Doer
  • Ruidu_Doer
  • 2015-12-21 14:20
  • 5124

用RecyclerView轻松实现gridview中itemview拖拽效果

以前一直认为支付宝中的gridview拖拽效果很牛逼,局限于当时不会使用RecyclerView,眼光一直在GridView上打转,各种自定义,可是效果都不理想,前几天开始接触RecyclerView,使我打开了一个新的方向。言归正传,进入正题。 项目中不会导入RecyclerView使用的jar包...
  • jiangjie4558
  • jiangjie4558
  • 2016-04-08 13:51
  • 997

RecyclerView的条目拖拽和条目滑动

public class MainActivity extends AppCompatActivity implements StartDragListener { private RecyclerView mRecyclerView; private ItemTouchHelpe...
  • qq_32671919
  • qq_32671919
  • 2017-07-31 15:50
  • 105

RecyclerView的拖动和滑动 第二部分 :拖块,Grid以及自定义动画

原文:Drag and Swipe with RecyclerView Part Two: Handles, Grids, and Custom Animations  转载请注明出处:http://www.jcodecraeer.com/a/anzhuokaifa/androi...
  • hanhailong726188
  • hanhailong726188
  • 2015-07-26 23:23
  • 7595

RecyclerView实现滑动和拖拽功能(带小例子)

前言: RecyclerView相对于ListView实现拖拽和滑动的效果要容易很多,今天写一个小程序,在上一篇文章 RecyclerView+CardView使用总结(带小例子) 基础上实现RecyclerView条目的上下拖拽和滑动删除,效果图如下: 第一步:设置拖动和滑动的回掉,让recyc...
  • IWantToHitRen
  • IWantToHitRen
  • 2017-03-10 16:31
  • 636

安卓里RecyclerView的拖拽滑动列表

RecyclerView是V7下的一个控件,它提供给了用户一种拔插式的体验,这个控件相比于ListView和GridView要更加的灵活与方便。那么下面我变用代码与注释解析的方法将这个类的功能一步步的来实现出来。 首先,我们先建一个类,让它继承RecyclerView.ViewHolder ...
  • ghdmao
  • ghdmao
  • 2016-06-18 20:51
  • 1285
    个人资料
    • 访问:1640503次
    • 积分:6595
    • 等级:
    • 排名:第4318名
    • 原创:52篇
    • 转载:0篇
    • 译文:1篇
    • 评论:1296条
    我的微信公众号
    欢迎关注我的公众号,不定期为您推送优选博文,生活趣事!
    关注我的微信公众号

    关注我的微博
    友情链接


    我的Github

    QQ交流群 547839514

    博客专栏