Android侧滑菜单-一个零耦合的侧滑菜单

【Android】毫无耦合性,一个Item根布局搞定 item侧滑删除菜单,像IOS那样简单的使用侧滑删除。 【Android自定义ViewGroup】不一样的轮子,巧用类变量解决冲突,像IOS那样简单的使用侧滑删除,一个控件搞定Android item侧滑删除菜单。 包含且不仅包含以下功能: 1 侧滑拉出菜单。 2 点击除了这个item的其他位置,菜单关闭。 3 侧滑过程中,不许父控件上下滑动。 4 多指同时滑动,屏蔽后触摸的几根手指。 5 不会同时展开两个侧滑菜单。 6 侧滑菜单时 拦截了长按事件。 阅读详情

话不多说先上图
在这里插入图片描述

目前功能如下

  • 支持启用或禁用侧滑菜单
  • 支持菜单在条目的左边或者右边
  • 支持滑动阻塞或非阻塞
  • 支持点击了menu后是否自动关闭menu
  • 支持menu打开和关闭的回调监听
  • 可快速打开和关闭menu

【点我】更多详细使用说明请移步github

写之前已经有自己的思路,后面看了几个博客的思路想碰撞下,看看会不会有新的启发,果不其然还是有的!!毕竟一个人的思维容易固定化;

我想要的结果就是,功能都在的情况下,不跟列表产生耦合!

先大致讲下思路:
自然是继承ViewGroup,重写它的onMeasure,onLayout;
xml中SwipeMenuLayout为最外层的布局,嵌套的view,第一个view是列表的itemView,之后的都是menuItem;
onMeasure中需要让第一个itemView的宽度为Match_parent;
onLayout中需要根据菜单在左在右的boolean值来控制menu的布局位置;
剩下的就是在dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent中来处理手指的事件分发逻辑了。

下面贴下主要代码;

onMeasure中第一个view必须是Match_parent,高度的话如果itemView是wrap_content,就是子view中最高的一个作为SwipeMenuLayout的高度

	@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取测量模式
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //内容view的宽度
        int contentWidth = 0;
        int contentMaxHeight = 0;
        mMenuWidth = 0;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childAt = getChildAt(i);
            if (childAt.getVisibility() == View.GONE) {
                continue;
            }
            LayoutParams layoutParams = childAt.getLayoutParams();
            if (i == 0) {
                //让itemView的宽度为parentView的宽度
                layoutParams.width = getMeasuredWidth();
                mContentView = childAt;
            }
            //测量子view的宽高
            measureChild(childAt, widthMeasureSpec, heightMeasureSpec);
            //如果parentView测量模式不是精准的
            if (heightMode != MeasureSpec.EXACTLY) {
                contentMaxHeight = Math.max(contentMaxHeight, childAt.getMeasuredHeight());
            }
            //child测量结束后才能获取宽高
            if (i == 0) {
                contentWidth = childAt.getMeasuredWidth();
            } else {
                mMenuWidth += childAt.getMeasuredWidth();
            }
        }
        //取最大值 重新测量
        int height = Math.max(getMeasuredHeight(), contentMaxHeight);
        setMeasuredDimension(contentWidth, height);
    }

onLayout中根据isEnableLeftMenu来确定menu是从左边开始布局还是从右边开始布局

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        int pLeft = getPaddingLeft();
        int pTop = getPaddingTop();
        int left = 0;
        int right = 0;

        for (int i = 0; i < childCount; i++) {
            View childAt = getChildAt(i);
            if (childAt.getVisibility() == View.GONE) {
                continue;
            }
            if (i == 0) {
                childAt.layout(pLeft, pTop, pLeft + childAt.getMeasuredWidth(), pTop + childAt.getMeasuredHeight());
                left += pLeft + childAt.getMeasuredWidth();
            } else {
                //放置左侧
                if (isEnableLeftMenu) {
                    childAt.layout(right - childAt.getMeasuredWidth(), pTop, right, pTop + childAt.getMeasuredHeight());
                    right -= childAt.getMeasuredWidth();
                } else {
                    //放置右侧
                    childAt.layout(left, pTop, left + childAt.getMeasuredWidth(), pTop + childAt.getMeasuredHeight());
                    left += childAt.getMeasuredWidth();
                }
            }
        }
    }

下面是主要的一个事件分发的逻辑,如果有对这个分发走向不太清楚的可以参考下这边文章:图解 Android 事件分发机制
贴一下图吧,先大致看下
在这里插入图片描述
这个流程图走向很清晰了,我们在onInterceptTouchEvent的move中判断滑动距离,如果滑动距离大于一个值,我们认为它是滑动了,不是点击,这个值可以通过如下方式获取

 //获取滑动的最小值,大于这个值就认为他是滑动  默认是8
 mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();

再贴下事件分发的逻辑代码,基本都有注释在,看起来也很清晰;

	@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mFirstRawX = ev.getRawX();
                getParent().requestDisallowInterceptTouchEvent(false);
                //关闭上一个打开的SwipeMenuLayout
                chokeIntercept = false;
                if (null != mCacheView) {
                    if (mCacheView != this) {
                        mCacheView.closeMenuAnim();
                        chokeIntercept = isOpenChoke;
                    }
                    //屏蔽父类的事件,只要有一个侧滑菜单处于打开状态, 就不给外层布局上下滑动了
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                //多指触摸状态改变
                isFingerTouch = false;
                //如果已经侧滑出菜单,菜单范围内的点击事件不拦截
                if (Math.abs(getScrollX()) == Math.abs(mMenuWidth)) {
                    //菜单范围的判断
                    if ((isEnableLeftMenu && ev.getX() < mMenuWidth)
                            || (!isEnableLeftMenu && ev.getX() > getMeasuredWidth() - mMenuWidth)) {
                        //点击菜单关闭侧滑
                        if (isClickMenuAndClose) {
                            closeMenuAnim();
                        }
                        break;
                    }
                    //否则点击了item, 直接动画关闭
                    closeMenuAnim();
                    return true;
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
	
	@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (!this.isEnableSwipe) {
            return super.onInterceptTouchEvent(ev);
        }
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //多跟手指的触摸处理  isFingerTouch为true的话 表示之前已经有一个down事件了,
                if (isFingerTouch) {
                    return true;
                } else {
                    isFingerTouch = true;
                }
                //第一个触点的id, 此时可能有多个触点,但至少一个,计算滑动速率用
                mPointerId = ev.getPointerId(0);
                mLastRawX = ev.getRawX();
                break;
            case MotionEvent.ACTION_MOVE:
                //大于系统给出的这个数值,就认为是滑动了 事件进行拦截,在onTouch中进行逻辑操作
                if (Math.abs(ev.getRawX() - mFirstRawX) >= mScaledTouchSlop) {
                    longClickable(false);
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

	@Override
    public boolean onTouchEvent(MotionEvent ev) {
        //如果关闭了侧滑 直接super
        if (!this.isEnableSwipe) {
            return super.onTouchEvent(ev);
        }
        acquireVelocityTracker(ev);
        switch (ev.getAction()) {
            case MotionEvent.ACTION_MOVE:
                //有阻塞
                if (chokeIntercept) {
                    break;
                }
                //计算移动的距离
                float gap = mLastRawX - ev.getRawX();
                //view滑动
                scrollBy((int) (gap), 0);
                if (Math.abs(gap) > mScaledTouchSlop || Math.abs(getScrollX()) > mScaledTouchSlop) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                //超过范围的话--->归位
                //目前是右滑的话 (菜单在左边)
                if (isEnableLeftMenu) {
                    if (getScrollX() < -mMenuWidth) {
                        scrollTo(-mMenuWidth, 0);
                    } else if (getScrollX() > 0) {
                        scrollTo(0, 0);
                    }
                } else {
                    if (getScrollX() < 0) {
                        scrollTo(0, 0);
                    } else if (getScrollX() > mMenuWidth) {
                        scrollTo(mMenuWidth, 0);
                    }
                }
                //重新赋值
                mLastRawX = ev.getRawX();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                //unitis值为1000(毫秒)时间单位内运动了多少个像素 正负最多为mScaledMaximumFlingVelocity
                mVelocityTracker.computeCurrentVelocity(1000, mScaledMaximumFlingVelocity);
                float velocityX = mVelocityTracker.getXVelocity(mPointerId);
                //释放VelocityTracker
                recycleVelocityTracker();
                if (!chokeIntercept && Math.abs(ev.getRawX() - mFirstRawX) >= mScaledTouchSlop) {
                    //获取x方向的运动速度
                    Log.d(TAG, "onTouchEvent: " + velocityX);
                    //滑动速度超过1000  认为是快速滑动了
                    if (Math.abs(velocityX) > 1000) {
                        if (velocityX < -1000) {//左滑了
                            if (!isEnableLeftMenu) {
                                //展开Menu
                                expandMenuAnim();
                            } else {
                                //关闭Menu
                                closeMenuAnim();
                            }
                        } else {//右滑了
                            if (!isEnableLeftMenu) {
                                //关闭Menu
                                closeMenuAnim();
                            } else {
                                //展开Menu
                                expandMenuAnim();
                            }
                        }
                    } else {
                        //超过菜单布局的40% 就展开 反之关闭
                        if (Math.abs(getScrollX()) > mMenuWidth * 0.4) {//否则就判断滑动距离
                            //展开Menu
                            expandMenuAnim();
                        } else {
                            //关闭Menu
                            closeMenuAnim();
                        }
                    }
                    return true;
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

剩下的部分就是一些不是很关键的了,需要看的话请移步到GitHub中Android侧滑菜单-SwipeMenuLayout

SwipeMenuLayout, swipemnulayout一、承办方、承办方、xml。 立即下载

相关推荐

java实现mavlink协议解析与封包上传数据,实现mavlink数据交互,附带代码、详解

本篇文章介绍了使用java来进行mavlink协议解析,封包与解包的过程,并附带源码供大家使用。MAVLink(Micro Air Vehicle Link,微型空中飞行器链路通讯协议)是无人飞行器与地面站(Ground Control Station ,GCS)或云端服务器之间通讯,以及无人飞行器之间通讯最常用的协议。它已经在PX4、APM、PIXHAWK和Parrot AR.Drone飞控平台上进行了大量测试。

qq_30279827的博客 1449

探索SwipeMenuLayout:一款高效易用的Android滑动菜单库

探索SwipeMenuLayout:一款高效易用的Android滑动菜单库 在Android应用开发中,滑动菜单是一种常见的交互设计,它为用户提供了一种简洁、直观的方式来访问和操作应用的不同部分。是一款开源的Android库,专门用于实现这种功能。它的目标是让开发者能够快速地集成并自定义滑动菜单,无需从头开始编写复杂的代码。 项目简介 SwipeMenuLayout由开发者ljphawk创建,旨在...

gitblog_00093的博客 704

异常数据处理——3σ原则、箱线图

异常数据处理——3σ原则、箱线图 用不包括异常值的剩余数据的均值替换、删除DataFrame中某列有异常值的整行等方法

杨老师的小跟班(*∩_∩*) 9033

SwipeMenuLayout列表侧滑菜单

效果图如下: 1、引用SwipeMenuLayout类,网上一搜一大堆 2、布局中引用SwipeMenuLayout作为父类,其子类只能包含连个View或者ViewGroup(一个作为内容展示,另一个作为侧滑内容) 代码如下: <?xml version="1.0" encoding="utf-8"?> <com.leixiansheng.swipemenulayo...

Mr_Leixiansheng的博客 1101

(一)ANSYS WORKBENCH实践入门

ANSYS-workbench 有限元分析应用基础教程 (张晔)学习笔记

qq_51767372的博客 1万+

【医学图像分割网络】之Res U-Net网络PyTorch复现

【医学图像分割网络】之Res U-Net网络PyTorch复现 1.内容 U-Net网络算是医学图像分割领域的开山之作,我接触深度学习到现在大概将近大半年时间,看到了很多基于U-Net网络的变体,后续也会继续和大家一起分享学习。这次分享ResNet+U-Net的一个改进版,ResNet本身就是一个十分优秀的backbone,目前应用仍然十分广泛,我们融合进ResNet后,我们就可以使得U-Net进行迁移学习了。 1.将Resnet作为encoder替换U-Net原始结构 2.U-Net提出时间较早,当时还没

qq_39071739的博客 1万+

android 侧滑菜单退出,一个零耦合的侧滑菜单 SwipeMenuLayout

Android侧滑菜单-SwipeMenuLayoutSwipeMenuLayout是一个零耦合的侧滑菜单,使用方式及其简单!只需要正常编写xml布局文件即可。目前功能如下支持启用或禁用侧滑菜单支持菜单在条目的左边或者右边支持滑动阻塞或非阻塞支持点击了menu后是否自动关闭menu支持menu打开和关闭的回调监听可快速打开和关闭menu简单用例只需正常编写xml文件即可SwipeMenuLayou...

weixin_34869021的博客 508

【计算机组成原理实验】状态机实验

理解“状态机”概念,掌握“状态机”思想描述时序系统。通过下列三种基于D触发器的计数器结构,构建状态机的状态转移电路:环形计数器、扭环计数器和条件判断的扭环计数器。基于“状态机”原理,设计一个“交通灯”系统:控制红黄绿信号灯按照以下顺序循环点亮:“绿 黄 红 绿 ……”,红灯、绿灯和黄灯的亮灯持续时间可以通过拨码开关独立设置。红灯和绿灯是持续亮灯模式,黄灯则是闪烁亮灯模式。

Rover_jb的博客 1332

每天记录学习的新知识 :SwipeMenuLayout

转载:https://blog.youkuaiyun.com/u014644594/article/details/83864652 使用方式: <?xml version="1.0" encoding="utf-8"?> <com.yoshin.company.blogdemo.recycler.SwipeMenuLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swipeMe

嗯... 968

二十一、CANdelaStudio深入-SPRMIB的配置

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio的肯定响应抑制位配置,欢迎各位朋友订阅、评论,可以提出宝贵意见,以持续输出更好的作品,同时会根据评论、私信不定期更新文章内容,以及专栏目录。订阅该专栏的朋友可私信小编免费答疑解惑(^U^)ノ~YO CANdelaStudio-从入门到深入目录 文章目录CANdelaStu

诊断协议那些事儿's blog 1870

OpenHarmony AI系列:神经网络处理单元(NPU)驱动架构与实现详解

NPU(Neural Processing Unit,神经网络处理单元)是专门为神经网络计算设计的硬件加速器。在OpenHarmony系统中,NPU驱动是通过NNRT(Neural Network Runtime,神经网络运行时)框架实现的,它作为中间桥梁连接上层AI推理框架和底层NPU硬件,实现AI模型的跨芯片推理计算。

m0_67505153的博客 665

android tv侧滑菜单,SwipeMenuLayout一个零耦合的侧滑菜单

Android侧滑菜单-SwipeMenuLayoutSwipeMenuLayout是一个零耦合的侧滑菜单,使用方式及其简单!只需要正常编写xml布局文件即可。目前功能如下支持启用或禁用侧滑菜单支持菜单在条目的左边或者右边支持滑动阻塞或非阻塞支持点击了menu后是否自动关闭menu支持menu打开和关闭的回调监听可快速打开和关闭menu简单用例只需正常编写xml文件即可SwipeMenuLayou...

weixin_42549154的博客 608

2026年职业院校技能大赛中职移动应用与开发—数字生活APP原型设计参考答案(含素材)

本模块以产品原型为目标,考查参赛选手熟练收集、分析和归纳客户需求,清晰梳理业务流程,熟练使用UI设计软件进行产品UI/UE设计,掌握正确的UI配色方案,设计出符合业务逻辑的人体工学移动App 原型优秀作品。

我是网络安全兼技能大赛工程师,专注网络安全技术学习与技能大赛实战!持续分享最新的技术文章,帮你少走弯路,一起在技术赛道上进步~ 568

【Android】毫无耦合性,一个Item根布局搞定 item侧滑删除菜单,像IOS那样简单的使用侧滑删除。(1)

在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。

FZBH_Q的博客 999

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

终极Android侧滑菜单解决方案:SwipeDelMenuLayout让你的应用秒变丝滑✨

SwipeDelMenuLayout是史上最简单的Android侧滑菜单库,0耦合设计支持任意ViewGroup,让开发者轻松实现高仿QQ、iOS风格的侧滑删除功能。本文将带你快速掌握这个强大工具的使用方法,从基础集成到高级定制,让你的应用交互体验瞬间提升一个档次! ## ????为什么选择SwipeDelMenuLayout?三大核心优势解析 ### 1️⃣ 零耦合架构:开箱即用的侧滑菜单 传...

gitblog_00731的博客 1121

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

5分钟掌握Android侧滑菜单开发:SwipeDelMenuLayout完整指南

想要为你的Android应用添加优雅的侧滑菜单功能吗?SwipeDelMenuLayout是一个零耦合、支持任意ViewGroup的侧滑菜单控件,让你轻松实现类似QQ和iOS的交互体验。😊 ## ✨ 什么是SwipeDelMenuLayout? **SwipeDelMenuLayout**是史上最简单的侧滑菜单实现方案,它最大的特点就是**0耦合**,不需要依赖特定的父布局。无论是Recyc

gitblog_00296的博客 427

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

【Android】史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2401_85493350的博客 831

高效Android侧滑菜单解决方案:SwipeDelMenuLayout实战指南

SwipeDelMenuLayout是一款专为Android开发者打造的高效侧滑菜单解决方案,以0耦合设计著称,支持任意ViewGroup,帮助开发者轻松实现高仿QQ和iOS风格的侧滑删除功能。无论是列表、网格还是自定义布局,都能快速集成侧滑交互,提升应用用户体验。 ## 为什么选择SwipeDelMenuLayout? ### 核心优势一览 ✨ - **零耦合设计**:不依赖特定父布局,可在

gitblog_00723的博客 472

SwipeDelMenuLayout实战:复杂业务场景下的最佳侧滑菜单实现方案

SwipeDelMenuLayout是史上最简单的Android侧滑菜单库,以0耦合设计支持任意ViewGroup,帮助开发者快速集成高仿QQ、iOS风格的侧滑删除功能。本文将深入探讨如何在复杂业务场景中充分发挥其特性,解决实际开发中的常见难题。 ## 核心优势:为什么选择SwipeDelMenuLayout? SwipeDelMenuLayout凭借其独特设计成为侧滑菜单解决方案的佼佼者:

gitblog_00858的博客 443
上一篇: ConstraintLayout的constraintDimensionRatio属性记录
下一篇: JS 在获取当前月的最后一天遇到的坑
L_junpeng
博客等级 码龄10年 9粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值