Android开发ViewDragHelper打造不一样的recyclerview

2024-02-29 08:08

本文主要是介绍Android开发ViewDragHelper打造不一样的recyclerview,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

前面我有一篇是讲到了viewdraghelper,http://blog.csdn.net/sw950729/article/details/53352587。对viewdraghelper不了解,可以看完再说。有人说viewdraghelper这个不就是个手势处理类么,怎么打造不一样的recyclerview?不不不,不要小瞧所有的手势处理,包括那啥GestureDetector也是。但是本文重点还是用viewdraghelper处理。处理啥呢?没错,就是recyclerview的侧滑删除功能。
网上侧滑的轮子不要太多,各种swipelayout。各种开源,各种嗨~。不过被众人评价“最爱作死的人”的我。不是很喜欢直接用轮子,轮子,有2种,一种是直接用别人的轮子,意味着别人的思路强加在你身上,另一种就是自己写轮子or改轮子,把你思路强加在别人身上。我更倾向于后者,所以导致,写这个布局的时候爬了好久的坑,综合了不知道几十个甚至上百个布局来改写。最后形成了自己的布局。下面我们一步步来进行分析以及实现。

侧滑删除的效果

这里写图片描述

思路

上图应该就是我们做侧滑的效果,就如QQ的一样。屏幕内是内容,右边是影藏的控件,需要通过滑动来进行打开和关闭。那么我们正常一个控件显示整个屏幕的时候我们无法滑动这个控件,但我们通过viewdraghelper可以实现整个布局的滑动。那么,我们需要考虑另外一种情况,那就是越界问题。其实viewdraghelper用一个方法可以帮助我们很好的处理越界问题。

使用布局

侧滑应该用什么布局写?自定义listview?linearlayout?framelayout?还是viewgroup?要不我来个新鲜的?自定义recyclerview如何?好像有点夸张了- - 根据效果我觉得还是通过自定义LinearLayout进行比较合理。

大致功能

1.侧滑显示侧滑菜单

2.点击侧滑区域以外的地方影藏侧滑菜单

3.侧滑时禁止上下滑动,同时上下滑动时也禁止左右滑动

4.不会同时打开2个菜单,这里有2种效果,一个是打开另一个的时候,手指松开关闭上一个打开的item。第二种则是高仿QQ,只要有侧滑菜单下次触摸直接关闭无法打开第二个item。So,我进行了高仿QQ的处理。

5.侧滑时无法进行点击事件,需要先关闭item才能进行对应的点击事件。

分析详解

主要我们需要实现上面5点,这样就可以实现一个完美的侧滑删除~~~下面我们一步步的来分析下如何实现。

两个子view

 protected void onFinishInflate() {super.onFinishInflate();if (getChildCount() != 2) {throw new NullPointerException("you only need two child view!");}itemView = getChildAt(0);hiddenView = getChildAt(1);}protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);hiddenViewWidth = hiddenView.getMeasuredWidth();}

我们需要在onFinishInflate进行对item的获取,这个就是xml映射完成之后获取对应item。然后在onsizechange进行宽度的获取。

侧滑显示侧滑菜单

这个是完完全全用viewdraghelper来实现。话不多说,我们直接看代码:

 ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {public boolean tryCaptureView(View view, int arg1) {return view == itemView;}public int clampViewPositionHorizontal(View child, int left, int dx) {if (child == itemView) {if (left > 0) {return 0;} else {left = Math.max(left, -hiddenViewWidth);return left;}}return 0;}public int getViewHorizontalDragRange(View child) {return hiddenViewWidth;}public void onViewPositionChanged(View changedView, int left, int top,int dx, int dy) {if (itemView == changedView) {hiddenView.offsetLeftAndRight(dx);} else {itemView.offsetLeftAndRight(dx);}invalidate();}public void onViewReleased(View releasedChild, float xvel, float yvel) {if (releasedChild == itemView) {if (xvel == 0 && Math.abs(itemView.getLeft()) > hiddenViewWidth / 2.0f || xvel < 0) {open();} else {close();}}}};

hiddenwidth是影藏区域的宽度,整个布局是item的全屏宽度。我们让这个布局可控宽度为hiddenwidth+itemwidth。这样我们就可以愉快的滑动了~而且还不会越界。

影藏侧滑菜单

我们需要点击侧滑以外的任何位置,让他影藏。那这个应该如何处理呢。我们需要通过onInterceptTouchEvent来进行拦截处理。具体代码如下:

  public boolean onInterceptTouchEvent(MotionEvent event) {boolean value = helper.shouldInterceptTouchEvent(event);//if you open is not the current item,closeif (!SWSlipeManager.getInstance().haveOpened(this)) {SWSlipeManager.getInstance().close();}switch (event.getAction()) {case MotionEvent.ACTION_DOWN:downIX = event.getX();downIY = event.getY();break;case MotionEvent.ACTION_MOVE:moveX = event.getX();moveY = event.getY();if (Math.abs(moveX - downIX) > 1 || Math.abs(moveY - downIY) > 1) {value = true;}break;}return value;}

SWSlipeManager这个是记录item的状态的,我们后面在说,这段具体代码就是只要有item打开了。我们就进行关闭处理。

滑动事件处理

其实就是在onViewPositionChanged里面就行处理,参数里有dx和dy。其实这个x,y的偏移量。所以我们需要在x!=0的时候就行拦截。所以onViewPositionChanged的代码改成了这样:

   public void onViewPositionChanged(View changedView, int left, int top,int dx, int dy) {if (dx != 0) {getParent().requestDisallowInterceptTouchEvent(true);}if (itemView == changedView) {hiddenView.offsetLeftAndRight(dx);} else {itemView.offsetLeftAndRight(dx);}invalidate();}

禁止同时打开多个item

上面我们说到了一个管理侧滑开关的管理类。下面看看我们如何进行具体的处理。先看看我们是怎么管理的:

public class SWSlipeManager {private SWSlipeLayout swSlipeLayout;private static SWSlipeManager SWSlipeManager = new SWSlipeManager();public static SWSlipeManager getInstance() {return SWSlipeManager;}public void setSwSlipeLayout(SWSlipeLayout swSlipeLayout) {this.swSlipeLayout = swSlipeLayout;}public void clear() {swSlipeLayout = null;}public void close() {if (swSlipeLayout != null) {swSlipeLayout.close();}}/*** if s==null means no item is open** @return ture means open else close*/public boolean haveOpened() {return swSlipeLayout != null;}/*** if s==null means no item is open** @return true means two item is not the same one and one item is open*/public boolean haveOpened(SWSlipeLayout s) {return swSlipeLayout != null && swSlipeLayout == s;}
}

这边我们通过来进行布局是否打开来进行处理。这边我们还要继续修改OnviewPositionChanged这个方法,修改完成如下:

 public void onViewPositionChanged(View changedView, int left, int top,int dx, int dy) {if (dx != 0) {getParent().requestDisallowInterceptTouchEvent(true);}if (itemView == changedView) {hiddenView.offsetLeftAndRight(dx);} else {itemView.offsetLeftAndRight(dx);}if (itemView.getLeft() != 0) {SWSlipeManager.getInstance().setSwSlipeLayout(SWSlipeLayout.this);} else {SWSlipeManager.getInstance().clear();}if (itemView.getLeft() == 0 && changeStatus != Status.Close) {changeStatus = Status.Close;} else if (itemView.getLeft() == -hiddenViewWidth && changeStatus != Status.Open) {changeStatus = Status.Open;}invalidate();}

这边我们进行对item.getleft()来处理我们的管理类,告诉它我们是打开了还是关闭了。既然是管理类,我们需要在打开的时候告诉它我们打开了。关闭的时候告诉它已经关闭了,需要清空。所以我们的打开和关闭需写成如下:

    /*** slide close*/public void close() {if (helper.smoothSlideViewTo(itemView, 0, 0)) {ViewCompat.postInvalidateOnAnimation(this);}SWSlipeManager.getInstance().clear();}/*** slide open*/public void open() {SWSlipeManager.getInstance().setSwSlipeLayout(this);if (helper.smoothSlideViewTo(itemView, -hiddenViewWidth, 0)) {ViewCompat.postInvalidateOnAnimation(this);}}

这边既然用到了ViewCompat.postInvalidateOnAnimation(this)这个方法,所以我们一定不能少了这个方法。我记得刚开始的时候我就在这边进坑里去了。

    public void computeScroll() {super.computeScroll();// start animationif (helper.continueSettling(true)) {ViewCompat.postInvalidateOnAnimation(this);}}

这样我们是不是就算彻底解决了。不不不。我们还需要在ontouchevent里面进行判断。怎能少了这一步的拦截。少了它。前功尽弃。我们看看如何处理ontouchevent的。

 public boolean onTouchEvent(MotionEvent event) {if (SWSlipeManager.getInstance().haveOpened(this)) {getParent().requestDisallowInterceptTouchEvent(true);} else if (SWSlipeManager.getInstance().haveOpened()) {getParent().requestDisallowInterceptTouchEvent(true);return true;}switch (event.getAction()) {case MotionEvent.ACTION_DOWN:downX = event.getX();downY = event.getY();break;case MotionEvent.ACTION_MOVE:float moveX = event.getX();float moveY = event.getY();float dx = Math.abs(moveX - downX);float dy = Math.abs(moveY - downY);if (dx > dy) {getParent().requestDisallowInterceptTouchEvent(true);}downX = moveX;downY = moveY;break;}helper.processTouchEvent(event);return true;}

事件问题

看似解决了所以的问题。but,你们难道忘了点击事件了么?如果我在侧滑的时候点击,此时进行的是item的关闭处理,还是点击事件?按照我们的写法是啥?没错,他会同时执行。所以我们需要在进行点击时候加一个判断。

text.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {if (SWSlipeManager.getInstance().haveOpened()) {SWSlipeManager.getInstance().close();} else {Toast.makeText(context, position + 1 + "", 1000).show();}}});

这样我们的布局就算解决了。下面看看我们是如何进行删除和置顶的。

text_delete.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {delete(holder.getLayoutPosition());SWSlipeManager.getInstance().close();}});text_top.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {SWSlipeManager.getInstance().close();setTop(holder.getLayoutPosition());}});

具体代码如下:

public void delete(int position) {list.remove(position);notifyItemRemoved(position);}public void setTop(int position) {list.add(0, list.get(position));list.remove(position + 1);notifyDataSetChanged();}

总结

这个布局我已经上传到了我的开源布局SWPullRecyclerLayout中。地址:SWPullRecyclerLayout
老铁们,喜欢的话,随手点个star。多谢 。
我进行了为了防止QQ类似的bug来进行了优化。那么QQ的bug是什么呢?没图说了瘠薄,下面上图:
这里写图片描述
这里写图片描述
就是当执行刷新的时候,进行侧滑,刷新成功后,侧滑没归位,刷新也没归位。而我的就没问题了。每次执行刷新和加载后,执行如下几句代码:

   public void OnRefreshing() {Log.i("angel", "OnRefreshing: 正在刷新");
//        recycler.setIsScrollRefresh(false);
//        recycler.setScrollTo(recycler.getTotal(), 0);
//        SWSlipeManager.getInstance().close();}public void OnLoading() {Log.i("angel", "OnLoading: 正在加载");
//        recycler.setIsScrollLoad(false);
//        recycler.setScrollTo(recycler.getTotal(), 0);
//        SWSlipeManager.getInstance().close();}

先后顺序可调整。
关于SWPullRecyclerLayout,大家使用中如有一切问题,可以进行反馈,我会进行优化。也可以提议,如果合理,我也会进行后续优化。

这篇关于Android开发ViewDragHelper打造不一样的recyclerview的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/758088

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【区块链 + 人才服务】区块链集成开发平台 | FISCO BCOS应用案例

随着区块链技术的快速发展,越来越多的企业开始将其应用于实际业务中。然而,区块链技术的专业性使得其集成开发成为一项挑战。针对此,广东中创智慧科技有限公司基于国产开源联盟链 FISCO BCOS 推出了区块链集成开发平台。该平台基于区块链技术,提供一套全面的区块链开发工具和开发环境,支持开发者快速开发和部署区块链应用。此外,该平台还可以提供一套全面的区块链开发教程和文档,帮助开发者快速上手区块链开发。

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧