android ItemTouchHelper 的使用和遇到的问题

转载请注明出处:https://www.jianshu.com/p/05680f83a471

# ItemTouchHelper 简介:

这是一个RecyclerView的工具,提供了drag & swipe 的功能,可以帮助我们处理RecyclerView中的Item的拖拽和滑动事件。

一、创建ItemTouchHelper:

//创建helper对象,callback监听recyclerView item 的各种状态
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
//关联recyclerView,一个helper对象只能对应一个recyclerView
itemTouchHelper.attachToRecyclerView(recyclerView);

二、创建callback的两种方式:

  1. ItemTouchHelper.Callback :
new Callback() {
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        // 有6个值来控制方向
            //控制拖拽的方向(一般是上下左右)
             int dragFlags= ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            //控制快速滑动的方向(一般是左右)
             int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
//还有两个flag 分别是 ItemTouchHelper.START 和 ItemTouchHelper.END ,原文的解释是:
//Horizontal direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout direction. Used for swipe & drag control.
// 横向方向,取决于 RecyclerView 的方向,与 LinearLayoutManager 的 layoutReverse 有关(暂时没有验证)
            return makeMovementFlags(dragFlags, swipeFlags);//计算movement flag值
        }

        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
          // 拖拽时,每移动一个位置就会调用一次。
          // 在此改变 dataList 被移动 item 的位置,并且刷新adapter
           if (recyclerView == null) return false;
                RecyclerView.Adapter adapter = recyclerView.getAdapter();
                if (adapter == null) return false;
                if (dataList != null) {
                    int from = viewHolder.getAdapterPosition();
                    endPosition = target.getAdapterPosition();//在这里我一直在刷新最后移动到的位置,以便接下来做其他操作
                    Collections.swap(dataList, from, endPosition);//数据交换位置
                    // 使用notifyItemMoved可以表现得更平滑,问题是 from ~ endPosition 间的item position 不会更新,并引发一系列角标混乱的问题,
                    //这个问题可以在后面的 onSelectedChanged()方法中解决。
                    // 在此做notifyItemMoved操作就足够了,notifyDataSetChanged() 和 notifyItemRangeChanged() 会打断 drag 操作。
                    adapter.notifyItemMoved(from, endPosition);
                    //其他操作
                    if (onMovedListener != null) {
                        onMovedListener.onMoved(dataList, from, endPosition);
                    }
                }
            return true; // true 可以拖拽,false 不能。
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        //滑动处理
        }
    }

要想能够拖拽还得将 isLongPressDragEnabled() 这个方法返回值置为 true,同理,滑动是 isItemViewSwipeEnabled()。这两个回调默认返回 true,如果不小心重写返回了false,就改过来吧。

  1. ItemTouchHelper.SimpleCallback :
new SimpleCallback(int dragFlags,int swipeFlags) {
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            return false;
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

        }

    }

ItemTouchHelper.SimpleCallback 继承自 ItemTouchHelper.Callback,仅仅是对两个触摸事件 flag 值设置的封装 , 提供了 set、get 方法,其他无异。

 public SimpleCallback(int dragDirs, int swipeDirs) {
            mDefaultSwipeDirs = swipeDirs;
            mDefaultDragDirs = dragDirs;
 }

//不需要再写 getMovementFlags() 方法
 @Override
 public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
            return makeMovementFlags(getDragDirs(recyclerView, viewHolder),
                    getSwipeDirs(recyclerView, viewHolder));
}

三、 处理拖拽后position错乱问题:

解决问题的根本就是在拖拽操作结束后,刷新 adapter。

  • 不能在 onMove 回调中直接刷新,会打断 drag 操作。
  • 在 onSelectedChanged() 或 clearView() 回调刷新。
  • 建议使用 notifyItemRangeChanged(int start,int end) 方法刷新,可以减少不必要的刷新。
  1. onSelectedChanged() , 只会回调两次:手势事件产生 和 手势抬起并且动画结束。
int startPosition;
int endPosition;
 @Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
      super.onSelectedChanged(viewHolder, actionState);
      
       if (viewHolder != null && actionState != ACTION_STATE_IDLE) {
           // 非闲置状态下,记录下起始 position
            startPosition = viewHolder.getAdapterPosition();
        }
        if(actionState == ACTION_STATE_IDLE){
           // 当手势抬起时刷新,endPosition 是在 onMove() 回调中记录下来的
            RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
            if (adapter != null) {
                adapter.notifyItemRangeChanged(Math.min(startPosition, endPosition), Math.abs(startPosition - endPosition) + 1);
             }
        }
}

在这里如果同时有 drag 和 swipe 两种情况,判断一下是 drag 还是 swipe 的结束好一些。

  1. clearView() , 只会触发一次:当手势抬起并且动画结束后回调,刷新操作放在这里也可以。
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    super.clearView(recyclerView, viewHolder);
}
  • Tips:clearViewon() 比 SelectedChanged() 后调用,所以在手势抬起后,actionState 变为 ACTION_STATE_IDLE,然后 viewHolder 会在 clearView 中被置为null。

写在最后

有什么问题欢迎留言

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341