Android自定义滑动删除效果的实现代码

 更新时间:2018年03月15日 08:30:59   作者:流浪远方YP  
这篇文章将从现有 Android 滑动删除的痛点,到搭建好一个基本的框架,到最终提供一份完整的 Demo为止,争取为读者提供最大的可定制化,需要的朋友可以参考下

先给大家展示下效果图,如果感觉不错,请参考实现代码:

序言

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

这篇文章将从现有 Android 滑动删除的痛点,到搭建好一个基本的框架,到最终提供一份完整的 Demo为止,争取为读者提供最大的可定制化

正文

一. 滑动删除的痛点

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

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

另外,现有的资料都是在自己的代码实现上讲解的,对于实现正真的定制化还是很有难度的,当我们想要实现自己想要的功能时,我们还需要去看懂一些不相关的处理逻辑

(2). 需要处理的细节

我一直觉得 QQ 在处理滑动删除上做的是相对比较好的,特别是从各种细节处理上,它基本上都能给出符合用户心理预期的响应,这里也是以 QQ 为例来介绍几种需要注意和处理的细节;当然,需要注意的地方很多,一一例举不太现实,具体的还是需要自己动手啦

侧滑过程中,DOWN 时得到焦点的 Item 在 MOVE 过程中失去了焦点应该怎么处理?(即对应上面的 现有资料中的不足 中的第2项);如下图所示,手指 DOWN 的时候得到焦点的是 Item 7, 但是之后手指在 MOVE 过程中,Item 7 失去了焦点;正如上面所说,此时还是应该交由该 Item 7 处理滑动事件(如果在 DOWN 的时候已经判为侧滑的话)

 

如果当前有 Item 正在侧滑,那么 RecyclerView 就不能再同时上下滑动
如果当前有 Item 处于打开状态,那么在下一次 DOWN 的时候应该先将其关闭,同时在 UP 之前,MOVE 事件都应该是无效的(对于这种情况,也可以按照自己的逻辑处理,如: 如果当前有 Item 处于打开状态,那么在下一次 DOWN 的时候应该先将其关闭,但是在关闭之后,在 UP 之前出现的 MOVE 事件也应该响应)
在一次 DOWN->MOVE...MOVE->UP 的完整过程中,一旦初始判断决定了应该是上下滑动或者 Item 的左右滑动之后,在 MOVE 过程中就不能改变,直至下一次新的判断过程为止(这种情况容易出现在用户在一次过程中反复的上下滑动时突然来一次左右滑动(或者反复的左右滑动过程中,突然来一次上下滑动))

二. 一个框架

(1). 使用 RecyclerView 搭建框架

1. 预备知识

RecyclerView 对外提供的接口已经比较完善,所以不需要再去继承 RecyclerView 来监听其 MotionEvent 事件
可以通过 RecyclerView 的 addOnItemTouchListener() 方法来实现对所有 MotionEvent 的拦截,其需要传入一个 RecyclerView.OnItemTouchListener 对象,这是一个 interface ,需要我们自己来实现逻辑,这里笔者写了一个大致的 Demo 先来看看其各个方法之间的联系

recyclerView.addOnItemTouchListener(new  RecyclerView.OnItemTouchListener() {
     @Override
     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
       switch (e.getAction()) {
         case MotionEvent.ACTION_DOWN: {
           Log.d("@HusterYP", String.valueOf("onInterceptTouchEvent DOWN"));
           break;
         }
         case MotionEvent.ACTION_MOVE: {
           Log.d("@HusterYP", String.valueOf("onInterceptTouchEvent MOVE"));
           break;
         }
         case MotionEvent.ACTION_UP: {
           Log.d("@HusterYP", String.valueOf("onInterceptTouchEvent UP"));
           break;
         }
       }
       return true;
     }
     @Override
     public void onTouchEvent(RecyclerView rv, MotionEvent e) {
       switch (e.getAction()) {
         case MotionEvent.ACTION_MOVE: {
           Log.d("@HusterYP", String.valueOf("onTouchEvent MOVE"));
           break;
         }
         case MotionEvent.ACTION_UP: {
           Log.d("@HusterYP", String.valueOf("onTouchEvent UP"));
           break;
         }
       }
     }
     @Override
     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
     }
   });

关于该 Demo 的代码可至笔者 Github 上下载执行测试;这里笔者就直接给出在 onInterceptTouchEvent 方法中返回不同值时的结论了:

如果在最后返回 false,那么 DOWN,MOVE,UP事件都是交给 onInterceptTouchEvent 处理可上下滚动
如果在最后返回 true,那么 onInterceptTouchEvent 只会接受到一个 DOWN,一个 MOVE;但是onTouchEvent 接收到剩下的 MOVE 和 UP; 不可上下滚动

如果最后返回 false,但是在 onInterceptTouchEvent 的 DOWN 判断中返回 true,这种情况同1
如果最后返回 false 或者 true,但是在 onInterceptTouchEvent 的 DOWN 判断中调用rv.setLayoutFrozen(true);方法,那么 onInterceptTouchEvent 只会收到一个 DOWN

如果在最后返回 false,但是在 onInterceptTouchEvent 的 MOVE 判断中 return true;的话,同情况2
那么通过上面的预备知识和结论,我们实现的滑动删除的思路也就渐渐清晰了:

最关键的是如何判断应该是 Item 的横向滑动还是 RecyclerView 的上下滑动,这里可以通过判断手指滑动的速度来判断: 即在 onInterceptTouchEvent 方法中的 MOVE 事件中去判断,如果 x 向速度大于 y 向速度,那么可以判断为是 Item 的横向滑动,直接 return true 即可,正如上面分析的那样,之后直接在 onTouchEvent 方法中处理 Item 的滑动逻辑即可;这里还有一点需要注意的是,在 onInterceptTouchEvent 的 MOVE 事件中判断时,对于一个完整的 DOWN->MOVE...MOVE->UP 过程,其实只需要,也只能执行一次判断,因为对于这样一个完整的过程,一旦在初始 MOVE 中将该过程判断为 Item 左右滑动或者 RecyclerView 上下滑动之后,中间就不可能突然改变,这对应上面 需要处理的细节 中的情况5;所以这里笔者是通过一个标志变量(flag)来实现的,需要注意的是在 UP 之后需要把 flag 置位,方便下一次判断

对于当手指 DOWN 时,已经有了一个 Item 处于打开状态,那么此时也应该分情况,当此时手指 DOWN 处仍然为该打开 Item 时,那么手指的移动情况就应该交给该 Item 来处理;如果此时手指 DOWN 的位置不是该打开 Item ,那么合理的处理是先关闭该 Item,之后在该过程中的 MOVE 事件还要不要响应,其实笔者觉得都是可以接受的;至于具体的细节处理是设置两个 ViewHolder 变量来记录(curHolder和oldHolder)即可,可在 onInterceptTouchEvent 中的 DOWN 事件中判断

至于 Item 的平滑滑动和添加各种动画之类的,读者可以自行决定,这个不是本文的重点

三. 一个可扩展的Demo

这里给出笔者实现的一个完整 Demo,代码中也有部分注释,可以结合本文再来理清一下逻辑
完整Demo代码可以到笔者 Github 下载

同时,读者也可以根据自己的实际需要,重新设置布局和重新添加一些自己的滑动逻辑;需要需要解释的是,这里笔者为了实现平滑移动,所以继承了 RelativeLayout 在实现了一个 MyRelativeLayout 类,即最外层布局,如下可知,笔者只是简单的在其中使用了一个 Scroller 类来实现平滑移动,其他也没有复杂的操作

public class MyRelativeLayout extends RelativeLayout {
private Scroller scroller;
 public MyRelativeLayout(Context context) {
   super(context);
   init(context);
 }
 public MyRelativeLayout(Context context, AttributeSet attrs) {
   super(context, attrs);
   init(context);
 }
 public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
   super(context, attrs, defStyleAttr);
   init(context);
 }
 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
   super(context, attrs, defStyleAttr, defStyleRes);
   init(context);
 }
 private void init(Context context) {
   scroller = new Scroller(context);
 }
 public void onScroll(int dx) {
   if (this.getScrollX() != 0) {
     scroller.startScroll(this.getScrollX(), 0, dx, 0);
     invalidate();
   }
 }
 @Override
 public void computeScroll() {
   super.computeScroll();
   if (scroller.computeScrollOffset()) {
     this.scrollTo(scroller.getCurrX(), 0);
     invalidate();
   }
 }
}

总结

以上所述是小编给大家介绍的Android自定义滑动删除效果的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

相关文章

  • 通俗易通讲解Android蓝牙键值适配

    通俗易通讲解Android蓝牙键值适配

    这篇文章介绍了Android蓝牙键值适配的方法,对大家的学习或工作具有一定的参考借鉴价值。需要的朋友可以收藏下,方便下次浏览观看
    2021-12-12
  • Android Parcleable接口的调用源码层分析

    Android Parcleable接口的调用源码层分析

    这篇文章主要给大家介绍了关于利用Kotlin如何实现Android开发中的Parcelable的相关资料,并且给大家介绍了关于Android Parcleable源码层问题,需要的朋友可以参考下
    2022-12-12
  • 详解Android App卸载后跳转到指定的反馈页面的方法

    详解Android App卸载后跳转到指定的反馈页面的方法

    这篇文章主要介绍了Android App卸载后跳转到指定的反馈页面的方法,关键点是相关线程要判断在目录被消除以前作出响应,需要的朋友可以参考下
    2016-04-04
  • Android中AutoCompleteTextView与MultiAutoCompleteTextView的用法

    Android中AutoCompleteTextView与MultiAutoCompleteTextView的用法

    这篇文章主要介绍了Android中AutoCompleteTextView与MultiAutoCompleteTextView的用法,需要的朋友可以参考下
    2014-07-07
  • Android studio中生成引用.aar和.jar的方法详解

    Android studio中生成引用.aar和.jar的方法详解

    这篇文章主要是讲解.aar的生成与引用,文中的内容属于完全基础性概念,对刚学习使用Android studio的朋友们很有帮助,有需要的可以参考学习,下面来一起看看吧。
    2016-09-09
  • Android EditText被软键盘遮盖的处理方法

    Android EditText被软键盘遮盖的处理方法

    android app新增了透明栏效果,结果发现键盘弹起后会遮盖屏幕底部的EditText,没有像想象中的调整窗口大小,并滚动ScrollView,将EditText显示在键盘上方。下面小编把解决方法记录一下,特此分享到脚本之家平台,感兴趣的朋友一起看看吧
    2016-10-10
  • Android-自定义控件之ListView下拉刷新的实现

    Android-自定义控件之ListView下拉刷新的实现

    本篇文章主要介绍了Android-自定义控件之ListView下拉刷新示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Android WebView实现全屏播放视频

    Android WebView实现全屏播放视频

    WebView是Android系统中的原生控件,其主要功能与前端页面进行响应交互,快捷省时地实现如期的功能,相当于增强版的内置浏览器。这篇文章主要介绍的是利用WebView实现全屏播放视频的功能,感兴趣的小伙伴可以了解一下
    2021-12-12
  • Android四大组件之Service服务详细讲解

    Android四大组件之Service服务详细讲解

    Android的服务是开发Android应用程序的重要组成部分。不同于活动Activity,服务是在后台运行,服务没有接口,生命周期也与活动Activity非常不同。通过使用服务我们可以实现一些后台操作,比如想从远程服务器加载一个网页等,下面来看看详细内容,需要的朋友可以参考下
    2022-07-07
  • 在android中如何用Java加载解析so

    在android中如何用Java加载解析so

    我们在android开发项目过程中都必然会更so加载打交道,那么so加载在系统中的顺序和流程是怎样的,我们就有必要对这个加载过程进行熟悉了解掌握
    2021-10-10

最新评论