Android开发学习菜菜Androidandroid

实现可自定义的Android滑动删除

2018-03-14  本文已影响475人  HusterYP

序言

最近项目中需要用到滑动删除,然后去网上搜了一下,发现现有网上的各种解决办法各式各样,但是还是找不到一个能将所有细节和逻辑处理好的,至于滑动删除部分,我觉得处理的相对比较好的是 QQ(包括处理各种逻辑和细节);最终,苦寻无果,于是决定自己动手,丰衣足食

正文

一. 滑动删除的痛点

(1). 现有资料中的不足

  1. 笔者参阅了网上的一些博客,发现,这些博客中大多能够基本实现滑动删除,但是存在的问题是,对于面向用户实际使用而言,却是远远不够的
  2. 大多数博客实现的只是当手指 DOWN 的时候,通过判断左右滑动和上下滑动的距离之比来判断 Item 是否应该滑动;但是有一个问题就是,用户 DOWN 的时候获得焦点的 Item ,但是 MOVE 的时候手指离开了该 Item 的时候应该如何处理呢? 按照正常的用户逻辑,这时仍然应该是该 Item 处理滑动事件
  3. 最重要和最难的部分当然也是滑动冲突了,即不管使用 RecyclerView 还是使用 ListView 实现,其都存在处理上下滑动和左右滑动的冲突问题,很明显的是我们不能一味地拦截所有事件,因为对于上下滑动事件还需要交给 RecyclerView/ListView 来实现正常的上下滑动;滑动冲突部分如果处理不好的话会出现很明显的卡顿现象,同时也会出现不符合用户心理预期的响应,而这些都是用户不友好的
  4. 另外,现有的资料都是在自己的代码实现上讲解的,对于实现正真的定制化还是很有难度的,当我们想要实现自己想要的功能时,我们还需要去看懂一些不相关的处理逻辑

(2). 需要处理的细节

  1. 我一直觉得 QQ 在处理滑动删除上做的是相对比较好的,特别是从各种细节处理上,它基本上都能给出符合用户心理预期的响应,这里也是以 QQ 为例来介绍几种需要注意和处理的细节;当然,需要注意的地方很多,一一例举不太现实,具体的还是需要自己动手啦
  2. 侧滑过程中,DOWN 时得到焦点的 Item 在 MOVE 过程中失去了焦点应该怎么处理?(即对应上面的 现有资料中的不足中的第2项);如下图所示,手指 DOWN 的时候得到焦点的是 Item 7, 但是之后手指在 MOVE 过程中,Item 7 失去了焦点;正如上面所说,此时还是应该交由该 Item 7 处理滑动事件(如果在 DOWN 的时候已经判为侧滑的话)
失去焦点.png
  1. 如果当前有 Item 正在侧滑,那么 RecyclerView 就不能再同时上下滑动
  2. 如果当前有 Item 处于打开状态,那么在下一次 DOWN 的时候应该先将其关闭,同时在 UP 之前,MOVE 事件都应该是无效的(对于这种情况,也可以按照自己的逻辑处理,如: 如果当前有 Item 处于打开状态,那么在下一次 DOWN 的时候应该先将其关闭,但是在关闭之后,在 UP 之前出现的 MOVE 事件也应该响应)
  3. 在一次 DOWN->MOVE...MOVE->UP 的完整过程中,一旦初始判断决定了应该是上下滑动或者 Item 的左右滑动之后,在 MOVE 过程中就不能改变,直至下一次新的判断过程为止(这种情况容易出现在用户在一次过程中反复的上下滑动时突然来一次左右滑动(或者反复的左右滑动过程中,突然来一次上下滑动))

二. 一个框架

(1). 使用 RecyclerView 搭建框架

1. 预备知识

  1. 如果在最后返回 false,那么 DOWN,MOVE,UP事件都是交给 onInterceptTouchEvent 处理可上下滚动
  2. 如果在最后返回 true,那么 onInterceptTouchEvent 只会接受到一个 DOWN,一个 MOVE;但是onTouchEvent 接收到剩下的 MOVE 和 UP; 不可上下滚动
  3. 如果最后返回 false,但是在 onInterceptTouchEvent 的 DOWN 判断中返回 true,这种情况同1
  4. 如果最后返回 false 或者 true,但是在 onInterceptTouchEvent 的 DOWN 判断中调用rv.setLayoutFrozen(true);方法,那么 onInterceptTouchEvent 只会收到一个 DOWN
  5. 如果在最后返回 false,但是在 onInterceptTouchEvent 的 MOVE 判断中 return true;的话,同情况2
  1. 最关键的是如何判断应该是 Item 的横向滑动还是 RecyclerView 的上下滑动,这里可以通过判断手指滑动的速度来判断: 即在 onInterceptTouchEvent 方法中的 MOVE 事件中去判断,如果 x 向速度大于 y 向速度,那么可以判断为是 Item 的横向滑动,直接 return true 即可,正如上面分析的那样,之后直接在 onTouchEvent 方法中处理 Item 的滑动逻辑即可;这里还有一点需要注意的是,在 onInterceptTouchEvent 的 MOVE 事件中判断时,对于一个完整的 DOWN->MOVE...MOVE->UP 过程,其实只需要,也只能执行一次判断,因为对于这样一个完整的过程,一旦在初始 MOVE 中将该过程判断为 Item 左右滑动或者 RecyclerView 上下滑动之后,中间就不可能突然改变,这对应上面需要处理的细节中的情况5;所以这里笔者是通过一个标志变量(flag)来实现的,需要注意的是在 UP 之后需要把 flag 置位,方便下一次判断
  2. 对于当手指 DOWN 时,已经有了一个 Item 处于打开状态,那么此时也应该分情况,当此时手指 DOWN 处仍然为该打开 Item 时,那么手指的移动情况就应该交给该 Item 来处理;如果此时手指 DOWN 的位置不是该打开 Item ,那么合理的处理是先关闭该 Item,之后在该过程中的 MOVE 事件还要不要响应,其实笔者觉得都是可以接受的;至于具体的细节处理是设置两个 ViewHolder 变量来记录(curHolder和oldHolder)即可,可在 onInterceptTouchEvent 中的 DOWN 事件中判断
  3. 至于 Item 的平滑滑动和添加各种动画之类的,读者可以自行决定,这个不是本文的重点

三. 一个可扩展的Demo

上一篇 下一篇

猜你喜欢

热点阅读