Android侧滑栏(一)可缩放可一起移动的侧滑栏
在实际的各类App开发中,经常会需要做一个左侧的侧滑栏,类似于QQ这种。
今天这篇文章总结下自己在开发中遇到的这类可以跟随移动且可以缩放的侧滑栏。
一、实现原理
使用 HorizontalScrollView 实现一个水平方向的可滑动的View,左布局为侧滑栏,右布局为自己的主页内容。
来看下android的官方解释,我用谷歌翻译了:
用户可以滚动的视图层次结构的布局容器,允许其大于物理显示。 HorizontalScrollView 是一种 FrameLayout,这意味着您应该在其中放置一个包含要滚动的全部内容的子级;这个子级本身可能是一个具有复杂对象层次结构的布局管理器。经常使用的子级是水平方向的 LinearLayout,它呈现用户可以滚动的顶级项目的水平数组。
TextView 类还负责自己的滚动,因此不需要 HorizontalScrollView,但将两者结合使用可以在更大的容器中实现文本视图的效果。
HorizontalScrollView 仅支持水平滚动。对于垂直滚动,请使用 ScrollView 或 ListView。
属性
关键点:
1、用户可以滚动的视图层次结构的布局容器,允许其大于物理显示。证明就像我们平时的用到的垂直方向的scrollView嵌套几个列表一样。
2、HorizontalScrollView 是一种 FrameLayout,这意味着您应该在其中放置一个包含要滚动的全部内容的子级;这个代表你需要在HorizontalScrollView先放一个总布局,再在这个布局里放左右布局内容。
二、实现过程
第一步:xml布局
<?xml version="1.0" encoding="utf-8"?><wanwan.and.lx.lxslideviewdemo.SlideMenuLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/sliding"android:background="@mipmap/img_bg"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><LinearLayoutandroid:layout_width="200dp"android:layout_height="match_parent"android:layout_gravity="start"android:orientation="vertical"><RelativeLayoutandroid:id="@+id/sidebarLayout"android:layout_width="match_parent"android:layout_height="match_parent"><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/sidebar_image_app_icon"android:layout_width="80dp"android:layout_height="80dp"android:layout_centerHorizontal="true"android:layout_marginTop="106dp"android:scaleType="fitXY"android:src="@mipmap/ic_launcher" /><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/slide_item_privacy"android:layout_width="200dp"android:layout_height="57dp"android:layout_below="@id/sidebar_image_app_icon"android:layout_marginTop="30dp"><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/set_privacy_lock_img"android:layout_width="16dp"android:layout_height="16dp"android:layout_marginStart="32dp"android:src="@mipmap/privacy"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><androidx.appcompat.widget.AppCompatTextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="13.7dp"android:text="Privacy Policy"android:textColor="@color/black"android:textSize="17sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toEndOf="@id/set_privacy_lock_img"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/slide_item_share"android:layout_width="200dp"android:layout_height="57dp"android:layout_below="@id/slide_item_privacy"android:layout_marginTop="10dp"><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/set_share_lock_img"android:layout_width="16dp"android:layout_height="16dp"android:layout_marginStart="32dp"android:src="@mipmap/share"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><androidx.appcompat.widget.AppCompatTextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="13.7dp"android:text="Share"android:textColor="@color/black"android:textSize="17sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toEndOf="@id/set_share_lock_img"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/slide_item_update"android:layout_width="200dp"android:layout_height="57dp"android:layout_below="@id/slide_item_share"android:layout_marginTop="10dp"><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/set_update_lock_img"android:layout_width="16dp"android:layout_height="16dp"android:layout_marginStart="32dp"android:src="@mipmap/update"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><androidx.appcompat.widget.AppCompatTextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="13.7dp"android:text="Update"android:textColor="@color/black"android:textSize="17sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toEndOf="@id/set_update_lock_img"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout></RelativeLayout></LinearLayout><RelativeLayoutandroid:layout_width="match_parent"android:background="@color/white"android:layout_height="match_parent"><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/text"android:layout_width="wrap_content"android:layout_centerInParent="true"android:textStyle="bold"android:textSize="18sp"android:textColor="@color/black"android:layout_height="wrap_content"android:text="这是主页"/><androidx.appcompat.widget.AppCompatImageViewandroid:layout_marginTop="20dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@mipmap/set"android:layout_centerHorizontal="true"android:layout_below="@id/text"android:id="@+id/set"/></RelativeLayout></LinearLayout></wanwan.and.lx.lxslideviewdemo.SlideMenuLayout>
1.SlideMenuLayout其实就是HorizontalScrollView,这是个自定义控件,待会儿代码附上。
2.可以看到SlideMenuLayout只有一个子View,为LinearLayout,LinearLayout它是全屏且水平布局,且有两个子布局,分为左右。
第二步:自定义控件HorizontalScrollView
class SlideMenuLayout : HorizontalScrollView {/*** 当菜单页显示时,右侧内容页显示宽度*/private var menuRightWidth = 0private lateinit var menuView: Viewprivate lateinit var contentView: View/*** 用于处理飞速滑动*/private var gestureDetector: GestureDetectorvar isMenuOpen: Boolean = falseprivate var btn: AppCompatImageView? = null/*** 是否进行事件拦截*/private var isIntercept = falseconstructor(context: Context) : this(context, null)constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {gestureDetector = GestureDetector(getContext(), GestureDetectorListener())}//用于处理飞速滑动inner class GestureDetectorListener : SimpleOnGestureListener() {override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {//屏蔽向右滑动if (e2.x - e1.x > 30) {Log.e("TAG", "right, right, go go go --->")return true}if (abs(velocityY) > abs(velocityX)) {return false}if (isMenuOpen) {if (velocityX < 0) {closeMenu()return true}} else {if (velocityX > 0) {openMenu()return true}}return super.onFling(e1, e2, velocityX, velocityY)}}/*** 此方法在布局加载完毕时调用*/override fun onFinishInflate() {super.onFinishInflate()val linearLayout: LinearLayout = getChildAt(0) as LinearLayoutval childCount = linearLayout.childCountif (childCount != 2) {throw IllegalArgumentException("LinearLayout child size must be 2!")}menuView = linearLayout.getChildAt(0)val menuLayoutParams = menuView.layoutParamsmenuLayoutParams.width = getScreenWidth() - menuRightWidth - SizeUtils.dp2px(100f)menuView.layoutParams = menuLayoutParams//设置右侧内容页宽度为屏幕宽度contentView = linearLayout.getChildAt(1)linearLayout.removeView(contentView)val contentRelativeLayout = RelativeLayout(context)contentRelativeLayout.addView(contentView)val contentLayoutParams = contentView.layoutParamscontentLayoutParams.width = getScreenWidth()contentRelativeLayout.layoutParams = contentLayoutParamslinearLayout.addView(contentRelativeLayout)btn = contentView.findViewById(R.id.set)btn?.setOnClickListener {openMenu()}}override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {super.onLayout(changed, l, t, r, b)//默认情况下,应该全部展示内容页,关闭左侧菜单页scrollTo(menuView.measuredWidth, 0)}/*** 获取当前屏幕的宽度*/private fun getScreenWidth(): Int {return resources.displayMetrics.widthPixels}/*** 重写该方法,用于处理缩放和透明度效果*/override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {super.onScrollChanged(l, t, oldl, oldt)//滚动的时候,不停的回调 l 从屏幕宽度变化到 0val scale = 1 - l * 1f / menuView.measuredWidth //scale 从 0 到1//处理菜单页缩放和透明度menuView.pivotX = menuView.measuredWidth * 1fmenuView.pivotY = menuView.measuredHeight / 2fmenuView.scaleX = 0.5f + scale * 0.5fmenuView.scaleY = 0.5f + scale * 0.5fmenuView.alpha = 0.25f + 0.75f * scale//处理内容页缩放 缩放到0.7fcontentView.pivotX = 0fcontentView.pivotY = contentView.measuredHeight / 2fcontentView.scaleX = 0.7f + (1 - scale) * 0.3fcontentView.scaleY = 0.7f + (1 - scale) * 0.3f}override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {if (ev.action == MotionEvent.ACTION_MOVE) {return false}if (isMenuOpen) {//如果点击事件落在内容页,则进行拦截并关闭菜单页if (ev.x > menuView.measuredWidth) {//进行事件拦截,不触发button点击事件isIntercept = truereturn true} else {isIntercept = false}}return super.onInterceptTouchEvent(ev)}override fun onTouchEvent(ev: MotionEvent): Boolean {//当执行快速滑动时,后续不再执行if (ev.action == MotionEvent.ACTION_MOVE) {return false}if (gestureDetector.onTouchEvent(ev)) {return gestureDetector.onTouchEvent(ev)}when (ev.action) {MotionEvent.ACTION_UP -> {if (isIntercept) {closeMenu()return true}//当手指抬起时,判断左侧菜单栏应该展示开始关闭//判断逻辑:当滚动x > 屏幕一半是,菜单栏隐藏,否则展开
// if (mScrollX > getScreenWidth() / 2) {
// closeMenu()
// } else {
// openMenu()
// }
// return false}}return super.onTouchEvent(ev)}/*** 打开菜单*/fun openMenu() {smoothScrollTo(0, 0)isMenuOpen = true}override fun dispatchTouchEvent(me: MotionEvent?): Boolean {if (me != null) {this.gestureDetector.onTouchEvent(me)}return super.dispatchTouchEvent(me)}/*** 关闭菜单*/fun closeMenu() {smoothScrollTo(menuView.measuredWidth, 0)isMenuOpen = false}}
关键点:
1、所谓的控制左右滑动,用到的是 smoothScrollTo()方法
2、使用SimpleOnGestureListener来进行手势监听,并且把onTouchEvent和dispatchTouchEvent的部分事件交由其处理。使用重写的onFling方法,进行各类手势处理,由于需求原因,上面的代码我禁止掉了右滑,可根据自己实际需求进行开发。
3、使用onInterceptTouchEvent拦截部分事件。
4、重写onScrollChanged监听滑动时,对左右布局进行缩放,这样会显得更流畅些。
5、重写onFinishInflate方法,拿到左右子布局,对其设置layoutparam属性,点击事件,赋值等等。
tips:menuLayoutParams.width = getScreenWidth() - menuRightWidth - SizeUtils.dp2px(100f)
这个就是设置左侧侧滑栏宽度,因为一般都不会全屏,所以会拿屏幕宽度去减去自己想要的值来展示右侧主页的内容,这儿可以给menuRightWidth设定一个值,不过我需求固定了,就直接在这儿减去了SizeUtils.dp2px(100f)。
第三步:代码调用:
class MainActivity : AppCompatActivity() {private var setImg: AppCompatImageView? = nullprivate var sliding: SlideMenuLayout? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)setImg = findViewById(R.id.set)sliding = findViewById(R.id.sliding)setImg?.setOnClickListener {sliding?.openMenu()}}
}
代码全在这儿了,我就不贴github地址了。
三、实现效果
相关文章:

Android侧滑栏(一)可缩放可一起移动的侧滑栏
在实际的各类App开发中,经常会需要做一个左侧的侧滑栏,类似于QQ这种。 今天这篇文章总结下自己在开发中遇到的这类可以跟随移动且可以缩放的侧滑栏。 一、实现原理 使用 HorizontalScrollView 实现一个水平方向的可滑动的View,左布局为侧滑…...

简单程度与自负是否相关?探索STM32的学习价值
事实上,无论STM32是否简单并不重要,更重要的是我们能通过学习STM32获得什么。通过STM32,我们可以学习到许多知识:如果我们制作一个键盘或鼠标,我们可以学习USB协议。如果我们制作一个联网设备,我们需要学习…...

第二章:CSS基础进阶-part3:弹性例子布局
文章目录 Flex盒模型二、常见属性2.1 flex属性2.2 justify-content2.3 flex-wrap2.4 flex-flow2.5 align-items2.6 父容器-align-content Flex盒模型 1、普通盒模型 2、弹性盒布局 使用弹性盒布局能让容器的宽度跟随浏览器窗口的变化而变换 二、常见属性 2.1 flex属性 2.2 …...
函数与方法有区别?
有区别,当然是有区别。 不管是java、rust还是go,他们都是不一样的。 先看定义: 函数(Function) 是一段独立的代码块,用于执行特定的任务。函数可以被多次调用,并且可以接受参数和返回结果。在G…...

VMware vCenter忘记密码操作,和Linus原理一致
mount -o remount,rw / passwd root ## 修改 root 密码要选择对应账户## 输入新密码,再输入一次新密码 umount / ## 卸载根文件系统 reboot -f ## 重新引导 vCenter...

Bert详细学习及代码实现详解
BERT概述 BERT的全称是Bidirectional Encoder Representation from Transformers,即双向Transformer的Encoder,因为decoder是不能获要预测的信息的。在大型语料库(Wikipedia BookCorpus)上训练一个大型模型(12 层到 …...

Vue [Day7] 综合案例
核心概念回顾 state:提供数据 getters:提供与state相关的计算属性 mutations:提供方法,用于修改state actions:存放异步操作 modules:存模块 功能分析 https://www.npmjs.com/package/json-server#ge…...

基于R做宏基因组的进化树ClusterTree分析
写在前面 同上一篇的PCoA分析,这个也是基于公司结果基础上的再次分析,重新挑选样本,在公司结果提供的csv结果表上进行删减,本地重新分析作图 步骤 表格预处理 在公司给的ClusterTree的原始表格数据里选取要保留的样本…...

栈和队列修炼指南(基本操作+OJ练习)
栈和队列修炼指南 1. 栈 1. 1 概念及结构 栈:是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作。进行数据插入和删除操作的一端称为栈顶,另一端为栈底。 栈中的数据元素遵守后进先出原则(LIFO)原则 压栈:栈的…...

伪类和伪元素有何区别?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 伪类(Pseudo-class)⭐ 伪元素(Pseudo-element)⭐ 区别总结⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前…...

自动测试框架airtest应用一:将XX读书书籍保存为PDF
一、Airtest的简介 Airtest是网易出品的一款基于图像识别和poco控件识别的一款UI自动化测试工具。Airtest的框架是网易团队自己开发的一个图像识别框架,这个框架的祖宗就是一种新颖的图形脚本语言Sikuli。Sikuli这个框架的原理是这样的,计算机用户不需要…...
ValueError:The following settings are not supported :{‘username‘: ‘neo4j“}
py2neo版本不同所导致的问题,下面我通过一段代码说明该问题。 import py2neoif py2neo.__version__ 4.3.0:graph Graph(http://localhost:7474, username config.neo4j_username, password config.neo4j_password) elif py2neo.__version__ 2021.2.3:graph G…...

360安全卫士右下角广告弹窗太多怎么彻底关闭?
360安全卫士右下角广告弹窗太多怎么彻底关闭? 1、卸载360安全卫士,选择继续卸载,并点击下一步; 2、选择广告弹窗太多,并点击下一步; 3、然后被告知升级极速版永久去广告,可以点击一键去广告。 …...

链表有无环以及确定入环口详解
142.环形链表 II 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测…...
chrome插件开发实例08- 使用Vue.js开发chrome插件
目录 背景 演示 功能介绍 插件下载 注意写法: 背景 将 下面的两个插件 改写成vue.js , elementui 实现chrome插件开发实例0...

PCL 计算外接圆的半径
目录 一、算法原理1、计算公式2、主要函数3、源码解析二、代码实现三、结果展示四、参考链接本文由CSDN点云侠原创,原文链接。爬虫自重。 一、算法原理 1、计算公式...
Matlab实现神经网络SOM算法(附上完整仿真源码)
神经网络SOM算法是一种基于自组织的无监督学习算法,其全称为Self-Organizing Map,可以用来对数据进行聚类和可视化。本文将介绍如何使用Matlab实现神经网络SOM算法。 文章目录 一、准备工作二、数据准备三、SOM算法实现四、聚类结果分析五、总结六、完整…...

【遍历】非递归法 二叉树的前中后序遍历
文章目录 非递归法前序遍历后序遍历中序遍历 递归法DFS 非递归法 通过栈Stack来模拟递归。 前序遍历 LeetCode 144 前序遍历:1 2 3 定义:存放答案的List、栈Stack 将root入栈出栈:node,为null则舍弃将node放入list将node.r…...
Python将tiff转换成png
文章目录 问题描述解决方案压缩并转换参考文献 问题描述 base64 的 image/tiff 无法在页面直接展示,将其转换为 image/png 解决方案 from io import BytesIOfrom PIL import Imagewith Image.open(a.tiff) as image:bytesIO BytesIO()image.save(bytesIO, format…...
【大数据】-- 部署 Flink kubernetes operator
目录 1.说明 1.1 版本 1.2 kubernetes 环境 1.3 参考 2.安装步骤 2.1 安装本地 kubernetes 环境...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...