RecyclerView之使用ItemTouchHelper实现交互动画
来源:互联网 发布:java自学视频百度云盘 编辑:程序博客网 时间:2024/04/29 22:51
一、简述
RecyclerView默认就有item动画,例如在增加或删除item时,都会有一个条目间位移的动画,但本文要说的不是这个!!!本文的主角是v7包中的ItemTouchHelper,它跟RecyclerView结合后将会带来神奇的交互效果。示例如下:
效果还是比较酷炫的吧,上图中有四步操作:
- 长按item后拖动,与其他item交换位置
- 按住item右面的图标后拖动,与其他item交换位置
- 左滑item变透明并缩小,超出屏幕后,其他item补上
- 右滑item变透明并缩小,超出屏幕后,其他item补上
下面将一一实现出这些效果
二、初识ItemTouchHelper
1、创建ItemTouchHelper
// 创建ItemTouchHelper,并跟RecyclerView绑定mItemTouchHelper = new ItemTouchHelper(mCallback);mItemTouchHelper.attachToRecyclerView(mRv);
上面就是ItemTouchHelper在本例中出场的三行代码中的两行代码,但这并不能完成上面的效果,ItemTouchHelper只是一个中间人,它将ItemTouchHelper.Callback和RecyclerView连接起来,具体效果还需要由Callback实现。
2、自定义ItemTouchHelper.Callback
1)创建一个自己的Callback
继承ItemTouchHelper.Callback后必须实现如下三个方法。
public class MyItemTouchHelperCallback extends ItemTouchHelper.Callback { @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { return 0; } @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder targetViewHolder) { return false; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { }}
2)重写getMovementFlags()
getMovementFlags()是用来判断RecyclerView上的哪些方向操作交由ItemTouchHelper.Callback控制,详细介绍如下:
/** * 获取动作标识 * 动作标识分:dragFlags和swipeFlags * dragFlags:列表滚动方向的动作标识(如竖直列表就是上和下,水平列表就是左和右) * wipeFlags:与列表滚动方向垂直的动作标识(如竖直列表就是左和右,水平列表就是上和下) */@Overridepublic int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { // 如果你不想上下拖动,可以将 dragFlags = 0 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // 如果你不想左右滑动,可以将 swipeFlags = 0 int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; //最终的动作标识(flags)必须要用makeMovementFlags()方法生成 int flags = makeMovementFlags(dragFlags, swipeFlags); return flags;}
上面我让item的上下左右都交由ItemTouchHelper.Callback控制,看下效果:
可以看到左右有效,但上下无效。仔细想想也是,本来就是竖直滚动列表,如果上下都直接交给ItemTouchHelper.Callback控制了,那RecyclerView的列表滚动功能该怎么办?所以,要触发上下拖动的交互效果,肯定有其他开启的方式。
3)开启长按item拖动效果
直接重写ItemTouchHelper.Callback的isLongPressDragEnabled()。
/** * 是否开启item长按拖拽功能 */@Overridepublic boolean isLongPressDragEnabled() { return true;}
默认返回是false,重写返回true。现在看下效果如何:
长按后可以拖动了,但是这样不太方便,接下来实现按住item右图标进行拖动的效果。
4)开启按住图标拖动效果
ItemTouchHelper的startDrag(viewHolder)方法可以手动开启拖动效果,上面的ItemTouchHelper实例创建在Activity中,而图标实例在Adapter中,为了降低耦合,这里先写一个接口:
public interface ItemDragListener { void onStartDrags(RecyclerView.ViewHolder viewHolder);}
接口由Activity实现,在Adapter创建时传入
public class ItemTouchHelperActivity extends AppCompatActivity implements ItemDragListener { private ItemTouchHelper mItemTouchHelper; ... private void setRecyclerView() { mAdapter = new ItemTouchHelperAdapter(mData, this); ... } @Override public void onStartDrags(RecyclerView.ViewHolder viewHolder) { mItemTouchHelper.startDrag(viewHolder); }}
接口由Adapter的图标触摸时调用
public class ItemTouchHelperAdapter extends RecyclerView.Adapter<ItemTouchHelperAdapter.ItemTouchHelperViewHolder> { private List<String> mData; private ItemDragListener mItemDragListener; public ItemTouchHelperAdapter(List<String> data, ItemDragListener itemDragListener) { mData = data; mItemDragListener = itemDragListener; } ... @Override public void onBindViewHolder(final ItemTouchHelperViewHolder viewHolder, int position) { ... viewHolder.mIvDrag.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { mItemDragListener.onStartDrags(viewHolder); return false; } }); }}
这样在让图标在触摸时,间接的调用了mItemTouchHelper.startDrag(viewHolder),看下效果:
三、深入ItemTouchHelper
上面做到了item的上下拖动和左右滑动效果,但只是当前item的动画效果罢了,下面继续完成与其他item互动的效果吧。
1、上下拖动时与其他item进行位置交换
1)原理
其实ItemTouchHelper.Callback本身不具备将两个item互换位置的功能,但RecyclerView可以,我们可以在item拖动的时候把当前item与另一个item的数据位置交换,再调用RecyclerView的notifyItemMoved()方法刷新布局,同时,因为RecyclerView自带item动画,就可以完成上面的交互效果了。
2)实现
item拖动要在ItemTouchHelper.Callback中监听,而数据交换处理要在Adapter中进行,为了降低耦合,这里先写一个接口:
public interface ItemMoveListener { boolean onItemMove(int fromPosition, int toPosition);}
接口由Adapter实现
public class ItemTouchHelperAdapter extends RecyclerView.Adapter<ItemTouchHelperAdapter.ItemTouchHelperViewHolder> implements ItemMoveListener{ ... @Override public boolean onItemMove(int fromPosition, int toPosition) { //1、交换数据 Collections.swap(mData, fromPosition, toPosition); //2、刷新 notifyItemMoved(fromPosition, toPosition); return true; }}
接口在创建ItemTouchHelper.Callback时传入,在onMove()中调用
public class MyItemTouchHelperCallback extends ItemTouchHelper.Callback { ItemMoveListener mItemMoveListener; public MyItemTouchHelperCallback(ItemMoveListener itemMoveListener) { mItemMoveListener = itemMoveListener; } ... /** * 当item拖拽移动时触发 * * @param recyclerView * @param viewHolder 当前被拖拽的item的viewHolder * @param targetViewHolder 当前被拖拽的item下方的另一个item的viewHolder * @return */ @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder targetViewHolder) { return mItemMoveListener.onItemMove(viewHolder.getAdapterPosition(), targetViewHolder.getAdapterPosition()); }}
3)效果:
2、左右滑出屏幕时其他item补上
1)原理
相同的,只要在item滑出屏幕时,将对应的数据删掉,再调用RecyclerView的notifyItemRemoved()方法刷新布局即可。
2)实现
item滑动要在ItemTouchHelper.Callback中监听,而数据删除处理要在Adapter中进行,所以只需要加上面的接口中增加一个方法:
public interface ItemMoveListener { ... boolean onItemRemove(int position);}
接口由Adapter实现
public class ItemTouchHelperAdapter extends RecyclerView.Adapter<ItemTouchHelperAdapter.ItemTouchHelperViewHolder> implements ItemMoveListener{ ... @Override public boolean onItemRemove(int position) { //1、删除数据 mData.remove(position); //2、刷新 notifyItemRemoved(position); return true; }}
接口在ItemTouchHelper.Callback在onSwiped()中调用
/** * 当item侧滑出去时触发(竖直列表是侧滑,水平列表是竖滑) * * @param viewHolder * @param direction 滑动的方向 */@Overridepublic void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { mItemMoveListener.onItemRemove(viewHolder.getAdapterPosition());}
3)效果:
现在大部分效果已经实现,接下来是细节处理。
3、交互时背景变化
1)原理
在item被拖拽或侧滑时修改背景色,当动作结束后将背景色恢复回来,而ItemTouchHelper.Callback中正好有对应这两个状态的方法,分别是:onSelectedChanged()、clearView()。
2)实现
/** * 当item被拖拽或侧滑时触发 * * @param viewHolder * @param actionState 当前item的状态 */@Overridepublic void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { super.onSelectedChanged(viewHolder, actionState); //不管是拖拽或是侧滑,背景色都要变化 if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext().getResources().getColor(android.R.color.darker_gray));}/** * 当item的交互动画结束时触发 * * @param recyclerView * @param viewHolder */@Overridepublic void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext().getResources().getColor(android.R.color.white));}
3)效果
4、左右滑动时item渐变
1)分析及实现
在最开始的图中,当item被侧滑出去的过程中,可以看到item的透明度在渐渐变浅,高度在渐渐变小,其实就是让item执行了两种属性动画而已,在ItemTouchHelper.Callback中有一个方法可以拿到item被拖拽或滑动时的位移变化,那就是onChildDraw()方法,这样就很好办了,看如下代码实现:
@Overridepublic void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { //这句代码就是item拖拽和滑动效果的实现,所以这句不能省略 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); //我们只需要在左右滑动时,将透明度和高度的值变小(1 --> 0) if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { float value = 1 - Math.abs(dX) / viewHolder.itemView.getWidth(); viewHolder.itemView.setAlpha(value); viewHolder.itemView.setScaleY(value); }}
2)效果(有瑕疵)
可以看到,item在滑动过程中渐渐透明并高度缩小了,但是,我明明是删除了两条,怎么结果列表中多出来两条空白的数据!这又是为什么呢?
3)修复
其实上图中并不是多出了两条空白数据,它们是正常的数据,只是看不到了,这是因为RecyclerView条目(itemView)覆用导致的,前面在onChildDraw()方法中对itemView设置了透明和缩小,而一个列表中固定只有几个itemView而已,当那两个透明缩小的itemView被再次使用时,之前设置的透明度和高度比例已经是0,所以就出现了这种情况,解决方法也很简单,只要在item被移除后,将itemView的透明度和高度比例设置回来即可,代码如下:
/** * 当item的交互动画结束时触发 * * @param recyclerView * @param viewHolder */@Overridepublic void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); ... viewHolder.itemView.setAlpha(1); viewHolder.itemView.setScaleY(1);}
4)效果(完美)
到这里,最开始的所有交互效果都已经实现了。
最后附上Demo链接
https://github.com/GitLqr/MaterialDesignDemo
- RecyclerView之使用ItemTouchHelper实现交互动画
- Android ItemTouchHelper实现RecyclerView交互动画
- RecyclerView系列之三:使用ItemTouchHelper实现RecyclerView的条目交互特效
- RecyclerView之使用ItemTouchHelper和ItemTouchHelper.Callback实现条目拖拽排序
- RecyclerView爱恨情仇之ItemTouchHelper
- RecyclerView使用ItemTouchHelper
- Android使用ItemTouchHelper实现RecyclerView的item拖动位置交换
- RecyclerView使用ItemTouchHelper实现拖拽和侧滑删除
- 使用ItemTouchHelper和RecyclerView实现拖拽移动效果
- RecyclerView ItemTouchHelper
- RecyclerView+ItemTouchHelper实现拖拽滑动
- ItemTouchHelper 使用RecyclerView打造可拖拽的GridView
- Android使用ItemTouchHelper打造可拖拽的RecyclerView
- Android RecyclerView 使用 ItemTouchHelper 时异常
- RecyclerView打造可拖拽的GridView使用ItemTouchHelper
- 使用ItemTouchHelper类轻松实现RecyclerView的拖拽和侧滑
- 使用ItemTouchHelper轻松实现RecyclerView拖拽排序和滑动删除
- 使用ItemTouchHelper轻松实现RecyclerView拖拽排序和滑动删除
- Spring Boot1.4版本后@ConfigurationProperties注解舍弃location参数后的解决方式
- 学习笔记-----HTML与CSS之间的关系
- JDK动态代理与Dubbo自实现动态代理的研究
- Linux学习(一)
- MVC5 + EF6 入门完整教程
- RecyclerView之使用ItemTouchHelper实现交互动画
- list对象取出重复的数据
- telerik如何在项目中被引用
- js,html 监听页面滚动高度 点击返回顶部
- 关于杀毒的解决方案
- jboss 下配置https
- Oracle定时任务与存储过程
- linux 配置redis
- IIS配置常见的错误