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地址了。

三、实现效果

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/37306.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL-MGR报错MY-011526

问题背景: 单主MGR集群&#xff0c;主节点在服务器意外重启之后&#xff0c;无法重新加入现有MGR集群&#xff0c;报错误[MY-011526] [Repl] Plugin group_replication reported: This member has more executed transactions than those present in the group. Local transact…

2023年度漏洞预警

1 漏洞 漏洞是硬件&#xff0c;软件&#xff0c;协议的具体实现或系统安全策略上存在的缺陷。从而可以使用攻击者能够在破坏系统。 2 漏洞汇总数据 以下数据针对 23 年截至8月期间爆发的高危严重漏洞进行了数据统计和分析&#xff0c; 具体的数据如下所示&#xff1a; 漏洞…

Camx--概述

该部分代码主要位于 vendor/qcom/proprietary/ 目录下&#xff1a; 其中 camx 代表了通用功能性接口的代码实现集合&#xff08;CamX&#xff09;&#xff0c;chi-cdk代表了可定制化需求的代码实现集合&#xff08;CHI&#xff09;&#xff0c;从图中可以看出Camx部分对上作为H…

v3s平台学习

printf 应用程序 arm-linux-gnueabihf-gcc test.c 复制a.out 到 sd卡 /media/shen/rootfs/root 运行a.out 不显示 解决方法 https://blog.csdn.net/whatday/article/details/85137031/?utm_mediumdistribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~def…

Linux 性能分析之iostat命令详解

Linux 性能分析之iostat命令详解 iostat命令是IO性能分析的常用工具&#xff0c;其是input/output statistics的缩写。本文将着重于下面几个方面介绍iostat命令&#xff1a; iostat的安装iostat命令行选项说明iostat输出内容分析如何确定磁盘IO的瓶颈iostat实际案例 命令的安…

django boostrap html实现可拖拽的左右布局,鼠标拖动调整左右布局的大小或占比

一、实现的效果 最近需要在Django项目中,实现一个左右布局的html页面,页面框架使用的是boostrap。但这个布局不是简单的左右分栏布局,而是需要实现可以通过鼠标拖拽的方式动态调整左右两侧布局的大小和占比。效果大致如下: 一开始,页面分为左右两块布局: 鼠标放到中间的…

Python脚本之连接MySQL【四】

本文为博主原创&#xff0c;未经授权&#xff0c;严禁转载及使用。 本文链接&#xff1a;https://blog.csdn.net/zyooooxie/article/details/124640412 之前写了篇 Python脚本之连接MySQL【三】&#xff0c;日常使用过程中&#xff0c;代码实际有很多改动&#xff0c;特此更新…

阿里云SMS,APi接口返回错误码

API错误码 更新时间&#xff1a;2023-06-29 16:33提交缺陷 产品详情 相关技术圈 我的收藏 调用API接口失败时&#xff0c;会返回错误码。本文档为您提供API接口错误码列表&#xff0c;请根据错误码和对应错误信息排查问题。 错误码&#xff08;Code&#xff09; 错误信息…

【先进PID控制算法(ADRC,TD,ESO)加入永磁同步电机发电控制仿真模型研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

EJB基本概念和使用

一、EJB是什么&#xff1f; EJB是sun的JavaEE服务器端组件模型&#xff0c;是一种规范&#xff0c;设计目标与核心应用是部署分布式应用程序。EJB2.0过于复杂&#xff0c;EJB3.0的推出减轻了开发人员进行底层开发的工作量&#xff0c;它取消或最小化了很多(以前这些是必须实现)…

如何使用Flask-RESTPlus构建强大的API

如何使用Flask-RESTPlus构建强大的API 引言&#xff1a; 在Web开发中&#xff0c;构建API&#xff08;应用程序接口&#xff09;是非常常见和重要的。API是一种允许不同应用程序之间交互的方式&#xff0c;它定义了如何请求和响应数据的规范。Flask-RESTPlus是一个基于Flask的…

定量分析计算51单片机复位电路工作原理 怎么计算单片机复位电容和电阻大小

下面画出等效电路图 可以知道单片机内必然有一个电阻RX&#xff0c;为了简化分析&#xff0c;我们假设他是线性电阻&#xff08;不带电容&#xff0c;电感的支路&#xff09; 还有一个基础知识&#xff1a; 电容器的充电放电曲线&#xff1a; 还需要知道电容电压的变化是连续…

微信小程序data-item设置获取不到数据的问题

微信小程序data-item设置获取不到数据的问题 简单说明&#xff1a; 在微信小程序中&#xff0c;通过列表渲染使用wx:for根据数组中的每一项重复渲染组件。同时使用bindtap给每一项绑定点击事件clickItem&#xff0c;再通过data-item绑定数据。 **问题&#xff1a;**通过data-i…

观察者模式实战

场景 假设创建订单后需要发短信、发邮件等其它的操作&#xff0c;放在业务逻辑会使代码非常臃肿&#xff0c;可以使用观察者模式优化代码 代码实现 自定义一个事件 发送邮件 发送短信 最后再创建订单的业务逻辑进行监听&#xff0c;创建订单 假设后面还需要做其它的…

常见的一些BUG

常见的一些BUG&#xff0c;但实际上在编写代码时&#xff0c;我们应该尽可能避免这些类型的错误&#xff1a; 变量名与函数名冲突&#xff1a; def main(): print("Hello, World!") main 5 print("The value of main is:", main) 函数参数传递错误&…

取个对象值导致系统崩溃

取个对象值导致系统崩溃 前言 想必各位小伙经常在项目中遇到一些错误&#xff0c;取对象值的时候&#xff0c;经常报错,又或者某些项目突然就挂经常都是出现在一些对象取值上面&#xff0c;然后就被领导一顿训斥 报错分析 例如&#xff1a; 下面这个报错大家想必不会陌生&am…

最大交换(力扣)枚举 JAVA

给定一个非负整数&#xff0c;你至多可以交换一次数字中的任意两位。返回你能得到的最大值。 示例 1 : 输入: 2736 输出: 7236 解释: 交换数字2和数字7。 示例 2 : 输入: 9973 输出: 9973 解释: 不需要交换。 注意: 给定数字的范围是 [0, 10^8] 解题思路&#xff1a; 1、数最…

【量化课程】08_2.深度学习量化策略基础实战

文章目录 1. 深度学习简介2. 常用深度学习模型架构2.1 LSTM 介绍2.2 LSTM在股票预测中的应用 3. 模块分类3.1 卷积层3.2 池化层3.3 全连接层3.4 Dropout层 4. 深度学习模型构建5. 策略实现 1. 深度学习简介 深度学习是模拟人脑进行分析学习的神经网络。 2. 常用深度学习模型架…

山东布谷科技直播软件源码Nginx服务器横向扩展:搭建更稳定的平台服务

在直播软件源码平台中&#xff0c;服务器扮演着重要的角色&#xff0c;关系着视频传输、数据处理、用户管理等工作的顺利完成。随着互联网的迅猛发展&#xff0c;直播行业也随之崛起&#xff0c;全世界的人们都加入到了直播软件源码平台中&#xff0c;用户流量的增加让服务器的…

视频高效剪辑,轻松平均分割视频,生成高质量M3U8

您是否在处理视频剪辑时常常面临繁琐的切分工作&#xff1f;是否希望能够快速而精准地平均分割视频&#xff0c;并生成适用于在线播放的高质量m3u8文件&#xff1f;现在&#xff0c;我们的智能视频剪辑大师为您提供了一种简便而高效的解决方案&#xff01;无需复杂操作&#xf…