博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RecyclerView 知识梳理(5) ItemTouchHelper
阅读量:6452 次
发布时间:2019-06-23

本文共 7403 字,大约阅读时间需要 24 分钟。

一、概述

ItemTouchHelperRecyclerView的整个体系中,负责监听Item的手势操作,我们通过给它设置一个继承于ItemTouchHelper.Callback的子类,在其中处理ItemUI变化,就可以完成侧滑删除、拖动排序等操作,下面,我们分以下几部介绍:

  • API解析
  • 实战
  • 采用默认动画
  • 自定义侧滑删除动画

二、API分析

对于Item的手势操作分为两种:侧滑和拖动,如果需要支持这两种,那么需要给ItemTouchHelper传入一个ItemTouchHelper.Callback的子类,并把ItemTouchHelperRecyclerView关联起来,下面,我们先来介绍一下ItemTouchHelper.Callback个回调方法的含义:

控制相关

  • public boolean isLongPressDragEnabled() 是否可以通过长按来触发拖动操作,默认返回true,如果返回false,那么可以通过startDrag(ViewHolder)方法来触发某个特定Item的拖动的机制。
  • public boolean isItemViewSwipeEnabled() 是否可以对每个Item进行侧滑。
  • public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 返回对于某个ViewHolder可以移动的方向,可选的值有UP/DOWN/LEFT/RIGHT/START/END。对于纵向排列的线性布局而言,如果要支持上下拖动排序,那么就要标志位中就要包含UP&DOWN,而如果需要支持左滑删除,那么标志位中就要包含LEFT

结果相关

  • public abstract boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) 当某个被拖动的Item被从旧位置拖动到了新位置后回调,如果返回true,那么ItemTouchHelper就认为viewHolder已经被移动到了targetAdapter中的位置。
  • public abstract void onSwiped(ViewHolder viewHolder, int direction) 当某个Item被滑动到消失时回调,direction表示滑动的方向。

状态相关

  • public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState)Item的状态发生改变时,回调该方法,actionState的取值有ACTION_STATE_IDLE/ACTION_STATE_SWIPE/ACTION_STATE_DRAG

  • public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) 标志着用户对于某个Item的操作并且Item的动画结束,此时我们应该恢复它的状态,以保证它被重新使用的时候能正确地展现。

  • public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive)

  • Canvas:绘制RecyclerViewCanvas

  • dx, dy:偏移。

  • actionState:拖拽还是侧滑,对应ACTION_STATE_DRAGACTION_STATE_SWIPE

  • isCurrentlyActivetrue表示这个Item正在被用户所控制,false则表示它仅仅是在回到原本状态的动画过程当中。

  • public void onChildDrawOver(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) 和上面类似,只不过它是绘制在Item之上。

三、实战

3.1 使用系统默认效果

如果我们希望使用系统默认的效果,那么只需要做以下几步:

  • 继承于ItemTouchHelper.Callback编写自己的回调类,并在拖动和侧滑操作完成之后更新数据:
public class SimpleItemTouchHelper extends ItemTouchHelper.Callback {    private ItemTouchAdapter mAdapter;    public SimpleItemTouchHelper(ItemTouchAdapter adapter) {        mAdapter = adapter;    }    @Override    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {        Log.d("SimpleItemTouchHelper", "onSwiped, onMove, source=" + viewHolder.getAdapterPosition() + ",target=" + target.getAdapterPosition());        mAdapter.onItemDragged(viewHolder.getAdapterPosition(), target.getAdapterPosition());        return true;    }    @Override    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {        Log.d("SimpleItemTouchHelper", "onSwiped, direction=" + direction);        mAdapter.onItemSwiped(viewHolder.getAdapterPosition());    }    @Override    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {        return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);    }}复制代码
  • 编写数据操作的代码:
public class NormalAdapter extends RecyclerView.Adapter
implements ItemTouchAdapter { //...... @Override public void onItemDragged(int from, int to) { Collections.swap(mTitles, from, to); notifyItemMoved(from, to); } @Override public void onItemSwiped(int position) { mTitles.remove(position); notifyItemRemoved(position); }}复制代码
  • ItemTouchHelper.CallbackRecyclerView关联起来,看注释中的1,2,3步:
private void init() {        List
titles = new ArrayList<>(); for (int i = 0; i < 20; i++) { titles.add(String.valueOf(i)); } LinearLayoutManager layoutManager = new LinearLayoutManager(this); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_content); recyclerView.setLayoutManager(layoutManager); NormalAdapter adapter = new NormalAdapter(titles); //1.自定义的ItemTouchHeloer.Callback SimpleItemTouchHelper simpleItemTouchHelper = new SimpleItemTouchHelper(adapter); //2.利用这个Callback构造ItemTouchHelper ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchHelper); //3.把ItemTouchHelper和RecyclerView关联起来. itemTouchHelper.attachToRecyclerView(recyclerView); recyclerView.setAdapter(adapter); }复制代码

下面就是最终的效果:

3.2 自定义侧滑删除动画

当我们需要自定侧滑删除动画时,那么需要重写onChildDraw或者onChildDrawOver方法,在其中监听滑动距离的变化,并根据它来实时改变viewHolder中的UI,首先看效果:

  • 首先,我们需要重写Item的布局,它包含两层,顶层是普通状态的标题文案,而底层则是蓝色底的删除提示:
复制代码

接着,我们需要重写ItemTouchHelper.Callback

public class AdvancedItemTouchHelper extends ItemTouchHelper.Callback {    private ItemTouchAdapter mAdapter;    public AdvancedItemTouchHelper(ItemTouchAdapter itemTouchAdapter) {        mAdapter = itemTouchAdapter;    }    @Override    public boolean isLongPressDragEnabled() {        return false;    }    @Override    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {        return makeMovementFlags(0, ItemTouchHelper.LEFT);    }    @Override    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {        mAdapter.onItemSwiped(viewHolder.getAdapterPosition());    }    @Override    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {        return false;    }    @Override    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {        super.clearView(recyclerView, viewHolder);        ((NormalAdapter.NormalViewHolder) viewHolder).mTv.setTranslationX(0);    }    @Override    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {        NormalAdapter.NormalViewHolder mViewHolder = (NormalAdapter.NormalViewHolder) viewHolder;        int deleteWidth = mViewHolder.mDeleteLayout.getWidth();        float fraction = deleteWidth / (float) mViewHolder.itemView.getWidth();        mViewHolder.mTv.setTranslationX(dX * fraction);    }}复制代码

这里面有几点需要注意:

  • 为了让Item支持左滑删除,我们需要在getMovementFlags中返回ItemTouchHelper.LEFT标志位。
  • onChildDraw当中,通过传入的dX动态改变了普通文案的translationX,使得底层的删除提示能够漏出。
  • 在侧滑操作完成之后,通过Adapter来删除数据。
  • clearView中,需要把mTv重置为初始的状态。

最后,我们按照前面的方法,把它和RecyclerView关联起来:

private void init() {        List
titles = new ArrayList<>(); for (int i = 0; i < 20; i++) { titles.add("Item " + String.valueOf(i)); } LinearLayoutManager layoutManager = new LinearLayoutManager(this); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_content); recyclerView.setLayoutManager(layoutManager); NormalAdapter adapter = new NormalAdapter(titles); AdvancedItemTouchHelper advancedItemTouchHelper = new AdvancedItemTouchHelper(adapter); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(advancedItemTouchHelper); itemTouchHelper.attachToRecyclerView(recyclerView); recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); recyclerView.setAdapter(adapter); }复制代码

四、小结

自定义RecyclerView的手势动画,关键是要理解ItemTouchHelper.Callback中各回调函数的含义,再通过回调函数中传入的数值来动态改变viewHolder中保存的itemView以及其子View的展现形式,就可以做出各种绚丽的效果。

五、参考文献


更多文章,欢迎访问我的 Android 知识梳理系列:

  • Android 知识梳理目录:
  • 个人主页:
  • 个人知识总结目录:

转载地址:http://nxgwo.baihongyu.com/

你可能感兴趣的文章
gitlab的用户使用手册
查看>>
论Optimizer的工作模式ALL_ROWS&FIRST_ROWS
查看>>
生产环境高并发MySQL SQL语句优化案例
查看>>
Lync 小技巧-24-PDF 加密文件-转-Word-操作手册
查看>>
ASP.NET性能优化之分布式Session
查看>>
TaffyDB Introduction
查看>>
Piwik 1.9.1 发布,网站访问统计系统
查看>>
【转】ie6 png 透明终极解决方案
查看>>
CentOS6.5菜鸟之旅:关于搜索的shell命令
查看>>
扩展ViewFlow避免和ViewPager滑动冲突,同时支持无限循环,并完美和CircleFlowIndicator结合...
查看>>
VC++之自定义消息
查看>>
JAVA数组的定义及用法
查看>>
Robots协议具体解释
查看>>
eclipse设置显示代码行数(转)
查看>>
【设计模式】状态模式
查看>>
Python的全局变量
查看>>
在JTextField中监听回车键,并执行相应按钮
查看>>
封装用于解析NSDate的便利的类
查看>>
[OpenCV] 3、直线提取 houghlines
查看>>
C++ string char[] 转化
查看>>