如何在RecyclerView上面实现"拖放"和"滑动删除"-2
拖动手柄
在设计一个支持"拖放"的列表时, 通常提供一个在触摸时初始化拖拽的"拖动手柄". 因其可发现性和可用性而被Material Guidelines所推荐, 尤其是列表处于"可编辑模式"时.
首先更新item的布局(item_main.xml):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="?listPreferredItemHeight"
android:clickable="true"
android:focusable="true"
android:foreground="?selectableItemBackground"> <TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:textAppearance="?android:attr/textAppearanceMedium" /> <ImageView
android:id="@+id/handle"
android:layout_width="?listPreferredItemHeight"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|right"
android:scaleType="center"
android:src="@drawable/ic_reorder_grey_500_24dp" />
</FrameLayout>
用作"拖放手柄"的图片可以在Material Design Icon找到, 也可以方便地通过Android Material Design Icon Generator Plugin添加.
我们曾经提到过, 可以通过代码ItemTouchHelper.startDrag(RecyclerView.ViewHolder)来开启拖动. 所以我们要做的就是更新ViewHolder来包含新的手柄视图, 并设置一个简单的触摸事件接口, 以触发startDrag()方法.
我们需要定义一个接口来传递拖动事件.
public interface OnStartDragListener { /**
* Called when a view is requesting a start of a drag.
*
* @param viewHolder The holder of the view to drag.
*/
void onStartDrag(RecyclerView.ViewHolder viewHolder);
}
然后, 在ItemViewHolder中实现化手柄视图.
public final ImageView handleView;
public ItemViewHolder(View itemView) {
super(itemView);
// ...
handleView = (ImageView) itemView.findViewById(R.id.handle);
}
并且更新Adapter.
private final OnStartDragListener mDragStartListener; public RecyclerListAdapter(OnStartDragListener dragStartListener) {
mDragStartListener = dragStartListener;
// ...
}
@Override
public void onBindViewHolder(final ItemViewHolder holder,
int position) {
// ...
holder.handleView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEventCompat.getActionMasked(event) ==
MotionEvent.ACTION_DOWN) {
mDragStartListener.onStartDrag(holder);
}
return false;
}
});
}
完整的Adapter应该看起来像这个.
剩下的是把OnStartDragListener添加到Fragment.
public class RecyclerListFragment extends Fragment implements
OnStartDragListener { // ...
@Override
public void onViewCreated(View view, Bundle icicle) {
super.onViewCreated(view, icicle); RecyclerListAdapter a = new RecyclerListAdapter(this);
// ...
}
@Override
public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
mItemTouchHelper.startDrag(viewHolder);
}
}
但你运行之后, 可以看到这样的效果:
标示选中视图
在上一篇的基础示例中, 被拖拽的item事实上是被选中的, 但是没有可视化的标示. 由于显著的理由, 这是不受欢迎的, 但也很容易修复. 事实上, 在ItemTouchHelper的帮助下, 只要你的ViewHolder的itemView设置了background集合(selector), 就会得到相应的效果. 在Lollipop及之后的版本, item view的elevation在拖拽和滑动期间会增加. 而在之前的版本中, 滑动时会有fade效果. 看起来就像:
效果看起来不错, 但你可能想要更多的控制. 一种方法是, 无论任何时候ViewHolder被选中或者清空, 让item自己处理这种改变. 由此, ItemTouchHelper.Callback提供了两种回调.
onSelectedChanged(RecyclerView.ViewHolder, int). 每一次ViewHolder的状态, 变成drag(ACTION_STATE_DRAG)或者swipe(ACTION_STATE_SWIPE)时, 该方法就会被调用. 这时候是将ViewHolder的状态变成active的完美时刻.
clearView(RecyclerView, RecyclerView.ViewHolder). 在被拖拽的ViewHolder放下时, 或者是滑动操作取消或者完成时(ACTION_STATE_IDLE), 这里会是将ItemView状态设置为idle的最好的地方.
那么, 我们就把两者绑定在一起实现.
首先, 创建一个接口, 让目标ViewHolder实现:
/**
* Notifies a View Holder of relevant callbacks from
* {@link ItemTouchHelper.Callback}.
*/
public interface ItemTouchHelperViewHolder { /**
* Called when the {@link ItemTouchHelper} first registers an
* item as being moved or swiped.
* Implementations should update the item view to indicate
* it's active state.
*/
void onItemSelected(); /**
* Called when the {@link ItemTouchHelper} has completed the
* move or swipe, and the active item state should be cleared.
*/
void onItemClear();
}
接着, 触发相应的回调方法:
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
int actionState) {
// We only want the active item
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
if (viewHolder instanceof ItemTouchHelperViewHolder) {
ItemTouchHelperViewHolder itemViewHolder =
(ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected();
}
} super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder); if (viewHolder instanceof ItemTouchHelperViewHolder) {
ItemTouchHelperViewHolder itemViewHolder =
(ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
现在, 剩下的是让ItemViewHolder实现ItemTouchHelperViewHolder:
public class ItemViewHolder extends RecyclerView.ViewHolder
implements ItemTouchHelperViewHolder { // ...
@Override
public void onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY);
} @Override
public void onItemClear() {
itemView.setBackgroundColor(0);
}
}
考虑到这仅仅是一个示例, 我们仅仅是在item处于active状态时设置了灰色背景, 当item被清空时, 把灰色背景删除了. 如果你的Adapter和ItemTouchHelper紧密结对的话, 轻易地放弃这个设置也行, 转而可以直接在ItemTouchHelper.Callback转变item的状态.
Grid布局
如果你想在本项目中转而使用GridLayoutManager, 你会发现并没有正常地起作用. 原因以及修复原因都很简单: 必须告诉ItemTouchHelper我们想要支持左右拖拽. 在SimpleTouchHelperCallback中, 我们已经声明:
@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);
}
支持Grid布局所要求的唯一改变是dragFlags中添加左右方向:
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
但是, 对于grid而言, "滑动删除"并不是一个天然的功能模式, 所以你也许会写一些如下的代码:
@Override
public int getMovementFlags(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
Grid上面的上下左右"拖放"效果看起来如下:
自定义滑动动画
对我们而言, 在viewHolder拖拽和滑动的时候, ItemTouchHelper.Callback为我们提供了一个十分方便的方式来完全控制ViewHolder的动画. 因为ItemTouchHelper是一个ItemDecoration, 我们可以用类似的方式, 详细地了解视图的绘制(hook into the View drawing).
下面是一个简单的例子, 来覆盖默认的滑动动画, 来展示一个线性的fade效果.
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView,
ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
float width = (float) viewHolder.itemView.getWidth();
float alpha = 1.0f - Math.abs(dX) / width;
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
} else {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY,
actionState, isCurrentlyActive);
}
}
参数dX和dY代表选中视图的当前位置,
- -1.0f表示完完全全的从ItemTouchHelper.END到ItemTouchHelper.START的滑动.
- 1.0f表示完完全全的从ItemTouchHelper.START到ItemTouchHelper.END的滑动.
对于任何没有处理的actionState你都可以调用super的方法, 从而使用默认的动画.
总结
我们仅仅了解了自定义ItemTouchHelper所能做的事情中有趣的部分, 我希望能够在一篇文章中包含更多内容, 但是考虑到文章长度, 还是分开比较好.
如何在RecyclerView上面实现"拖放"和"滑动删除"-2的更多相关文章
- 如何在RecyclerView上面实现"拖放"和"滑动删除"-1
Android上面有许多的教程, 库和示例, 在RecyclerView上面实现"拖放"和"滑动删除"功能. 尽管有更新, 更好的方法可用, 但是大多数人依然使 ...
- RecyclerView拖拽排序和滑动删除实现
效果图 如何实现 那么是如何实现的呢?主要就要使用到ItemTouchHelper ,ItemTouchHelper 一个帮助开发人员处理拖拽和滑动删除的实现类,它能够让你非常容易实现侧滑删除.拖拽的 ...
- RecyclerView实现拖动排序和滑动删除功能
RecyclerView 的拖动排序需要借助一下 ItemTouchHelper 这个类,ItemTouchHelper 类是 Google 提供的一个支持 RecyclerView 滑动和拖动的一个 ...
- ItemTouchHelper(实现RecyclerView上添加拖动排序与滑动删除的所有事情)
简单介绍: ItemTouchHelper是一个强大的工具,它处理好了关于在RecyclerView上添加拖动排序与滑动删除的所有事情.它是RecyclerView.ItemDecoration的子类 ...
- RecyclerView借助ItemTouchHelper实现拖动和滑动删除功能
RecyclerView是官方推荐代替ListView的空间,怎样实现RecyclerView列表元素的拖动呢? 官方提供了ItemTouchHelper类使用过程例如以下: 定义ItemTouchH ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- Android学习之ItemTouchHelper实现RecylerView的拖拽以及滑动删除功能
今天在群里见大神们提到控件的拖动以及滑动删除的效果实现,就在网上找了资料ItemTouchHelper学习,并实现其功能.不胜窃喜之至,忍不住跟大家分享一下,如今就对学习过程做下简介.帮助大家实现这样 ...
- 写一个js向左滑动删除 交互特效的插件——Html5 touchmove
需求描述 需要实现类似QQ中对联系人的操作:向左滑动,滑出删除按钮.滑动超过一半时松开则自动滑到底,不到一半时松开则返回原处. 纯js实现 使用了h5的touchmove等事件,以及用js动态改变cs ...
- iOS UITableViewCell滑动删除
一般我们使用列表的形式展现数据就会用到UITableView.在熟练掌握了用UITableView展示数据以后,开发过程中可能会遇到需要删除数据的需求,我们想实现在一行数据上划动一下,然后出现一个删除 ...
随机推荐
- 比特(bit)、字,字节(B)存储单位之间的关系+其与操作系统位数的关系+不同编译器编译方式下数据类型的表示范围
1.在表示网络传输速度中与表示存储单位的不同: 表示存储单位时:1kB=1024B,但在网络中表示传输速度是1KB=1000B 2.下面介绍表示存储单位时的关系及其与操作系统位数的关系: 1B=8bi ...
- 通过判断cookie过期方式向Memcached中添加,取出数据(Java)
应用场景:在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够 ...
- redis的删除库应用(linux)
1.首先从linux进入redis的安装目录下 2.用redis-cli在Shell命令行下启动Redis客户端工具. 3.select 库名 进入到库下 4.flushdb 刷新当前库 redis的 ...
- java_流
转自:http://blog.csdn.net/hguisu/article/details/7418161 1.什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有 ...
- 5个Android开发中比较常见的内存泄漏问题及解决办法
android中一个对象已经不需要了,但是其他对象还持有他的引用,导致他不能回收,导致这个对象暂存在内存中,这样内存泄漏就出现了. 内存泄漏出现多了,会是应用占用过多的没存,当占用的内存超过了系统 ...
- 微信公众号开发总结(Node.js + express + winston)
关于订阅号.服务号.企业号 官方定位 订阅号:主要偏于为用户传达资讯(类似报纸杂志),认证后每天可以群发一条消息,可达到宣传效果,构建与读者之间更好的沟通和管理模式. 服务号:主要偏于服务交互(类似银 ...
- WebSockets介绍
Web sockets定义为在servers和clients之间的双向连接.意味着servers和clients可以同时交流并发送数据.这种协议是从底层就是双工连接.Web sockets技术上得到了 ...
- centos安装UCenter 和 UCenter_Home
1.搭建lamp环境yum –y install httpd php php-mysql mysql mysql-server 2启动服务 3.设置服务开机自动启动 4.上传UCEN ...
- [Selenium With C#学习笔记] Lesson-02 Web元素定位
使用Selenium来做自动化测试,一般的流程是:查找定位元素--->操作元素--->断言,那么第一步我们需要能够完成查找并定位元素,Selenium目前提供了8种基本定位方法,可根据实际 ...
- css伪元素用法大全
本文主要讲解css伪元素的用法,有需要的朋友可以阅读此文.本文讲解的伪元素有:before,after. 什么是伪元素(Pseudo element)? 伪元素不是真正的元素,不存在与文档之中,所以j ...