拖拽 RecyclerView
现在有很多使用 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 的。在下一篇中我们将对拖拽进行更加丰富的控制实现。
源代码
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
下一篇: 自动化截图-应用分发时的自动截图方案
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论