拖拽 RecyclerView

发布于 2024-09-11 01:46:40 字数 8409 浏览 15 评论 0

现在有很多使用 RecyclerView 实现 拖拽(drag & drop) 滑动消失(swipe-to-dismiss) 效果的教程、库、和示例代码。虽然现在有更新的、更好的方式可以实现但是大部分的代码仍旧使用了旧的 API View.OnDragListener 或者使用 Roman Nurik’s SwipeToDismiss 这个库中的处理方式。

有一部分使用了新的 API,但主要是依赖 GestureDetectors 和 onInterceptTouchEvent 或者实现的方式很复杂。实际上有非常简单的方式就可以把新特性添加到 RecyclerView 中。只需要一个类,并且它已经包含在 Android Support Library 之中。

ItemTouchHelper

ItemTouchHelper 是一个非常强大的工具类用于处理当你添加 drag&drop 和 swipe-to-dismiss 特性到 RecyclerView 时所关心的那些问题。它是 RecyclerView.ItemDecoration 的子类,意味着可以向已经存在的 LayoutManager 和 Adapter(!) 中添加任何东西,并且也可以处理已经存在的 item animations、type-restricted、drop settling animations 甚至更多。 在这篇文章我将示范一下 ItemTouchHelper 的简单实现,稍后,在这一系列的文章中我们将延伸扩展更多特性。

忽略开头

只对代码感兴趣? Github 源码: Android-ItemTouchHelper-Demo , Apk 下载:from here .

配置

首先我们需要对 RecyclerView 进行配置,如果没有准备好,请先更新你的 build.gradle,添加 RecyclerView 依赖。

compile 'com.android.support:recyclerview-v7:22.2.0'

在任意 RecyclerView.Adapter 和 LayoutManager 都可以使用 ItemTouchHelper,这篇文章代码的基本文件在这:

https://gist.github.com/iPaulPro/2216ea5e14818056cfcc

使用 ItemTouchHelper 和 ItemTouchHelper.Callback

为了使用 ItemTouchHelper,你必须得先创建ItemTouchHelper.Callback 。这是一个接口用于监听“move” 和 “swipe”的状态。也可用于你要控制所选的 view 的状态和重写默认动画。如果你只是想使用基本实现你可以使用系统提供的一个帮助类SimpleCallback ,但是本着学习的目的,我们还是自己来实现。

实现基本的 drag & drop 和 swipe-to-dismiss 必须实现以下主要的回调函数:


getMovementFlags(RecyclerView, ViewHolder)
onMove(RecyclerView, ViewHolder, ViewHolder)
onSwiped(ViewHolder, int)

使用两个帮助函数:

isLongPressDragEnabled()
isItemViewSwipeEnabled()

我们将逐个进行介绍.


@Override
public int getMovementFlags(RecyclerView recyclerView, 
        RecyclerView.ViewHolder viewHolder) {
    int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
    int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
    return makeMovementFlags(dragFlags, swipeFlags);
}

ItemTouchHelper 允许非常简单的判断屏幕事件的走向。必须重写 getMovementFlags() 来说明支持哪个方向的拖拽和滑动。使用帮助类 ItemTouchHelper.makeMovementFlags(int, int) 来管理 returned 标志。这样就可以在同一个方向进行拖拽和滑动了。


@Override
public boolean isLongPressDragEnabled() {
    return true;
}

ItemTouchHelper 可以只支持 drag 而不支持 swipe(or vice versa),所以你必须明确指出你希望支持的类型。为了支持长按 RecyclerView item 对其进行拖动, isLongPressDragEnabled() 函数实现应该返回 true. 此外在开始拖拽时 ItemTouchHelper.startDrag(RecyclerView.ViewHolder) 将会被调用。这个我们稍后再讨论。


@Override
public boolean isItemViewSwipeEnabled() {
    return true;
}

如果在 viwe 中可以随意滑动, isItemViewSwipeEnabled() 函数返回 true. 此外手动拖动时 ItemTouchHelper.startSwipe(RecyclerView.ViewHolder) 会被调用。

onMove()onSwiped() 负责主要数据的更新。故首先我们需要创建一个允许传递事件回调的接口。


public interface ItemTouchHelperAdapter {

    void onItemMove(int fromPosition, int toPosition);

    void onItemDismiss(int position);
}

ItemTouchHelperAdapter.java Gist

最简单的方式就是下面这样,让我们的 RecyclerListAdapter 实现 ItemTouchHelperAdapter。


public class RecyclerListAdapter extends 
        RecyclerView.Adapter<ItemViewHolder> 
        implements ItemTouchHelperAdapter {
// ... code from [gist]()

@Override
public void onItemDismiss(int position) {
    mItems.remove(position);
    notifyItemRemoved(position);
}

@Override
public void onItemMove(int from, int to) {
    Collections.swap(mItems, from, to);
    notifyItemMoved(from, to);
}

notifyItemRemoved()notifyItemMoved() 的调用是非常重要,因此 Adapter 是可以感受到这些变化的,同样重要的是要注意当我们改变 item 的 Position 的时候 view 的 index 也时刻在改变, 并不是在“drop”事件结束后再改变

现在我们回过头来创建我们自己的 SimpleItemTouchHelperCallback 并且必须 override onMove() 和 onSwiped()。首先添加一个构造函数和 Adapter 变量。


private final ItemTouchHelperAdapter mAdapter;

public SimpleItemTouchHelperCallback(
        ItemTouchHelperAdapter adapter) {
    mAdapter = adapter;
}

之后 override the remaining events and notify the adapter:


@Override
public boolean onMove(RecyclerView recyclerView, 
        RecyclerView.ViewHolder viewHolder, 
        RecyclerView.ViewHolder target) {

mAdapter.onItemMove(viewHolder.getAdapterPosition(), 
            target.getAdapterPosition());
    return true;
}

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, 
        int direction) {
    mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}

写完之后回调类应该像下面这样:


public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {

    private final ItemTouchHelperAdapter mAdapter;

    public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
        mAdapter = adapter;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, 
            ViewHolder target) {
        mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(ViewHolder viewHolder, int direction) {
        mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
    }

}

随着我们 Callback 的完成,再创建 ItemTouchHelper 并且调用 attachToRecyclerView(RecyclerView) (e.g. in MainFragment.java ):


ItemTouchHelper.Callback callback = 
    new SimpleItemTouchHelperCallback(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(recyclerView);

结束语

这是一种极其简单的对 ItemTouchHelper 的实现写法。但是应该清楚的知道,使用 RecyclerView 实现基本的拖拽和滑动消失效果是不需要引入三方 library 的。在下一篇中我们将对拖拽进行更加丰富的控制实现。

源代码

Android-ItemTouchHelper-Demo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

兮子

暂无简介

0 文章
0 评论
20 人气
更多

推荐作者

謌踐踏愛綪

文章 0 评论 0

开始看清了

文章 0 评论 0

高速公鹿

文章 0 评论 0

alipaysp_PLnULTzf66

文章 0 评论 0

热情消退

文章 0 评论 0

白色月光

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文