Android级联选择器,下拉菜单

news2025/2/26 0:31:12

近期android开发,遇到的需求,分享二个android可能用到的小组件

 下拉选择器:它的实现,主要是需要监听它依附的组件当前距离屏幕顶端的位置。

                     在显示下拉菜单中,如果需要点击上面有响应。可通过activity拿到decorview(activity.window.decorView),然后把下拉的view添加到decorView,然后设置该菜单距离顶端的上边距。如果需要先点击屏幕让菜单先消失,可搞一个全屏的下拉菜单,距顶端高度搞一个透明的view来填充(设置一个点击事件,处理decorView.remove该下拉组件)

object DropDownItemsUtils {
    const val DROP_DOWN_ID = -1000
    fun showDropDownView(activity: Activity,marginTopPx:Float,list:List<String>,selectIndex:Int = -1,onItemClick: ((pos: Int?) -> Unit)?=null){
        val decorView = activity.window.decorView as ViewGroup
        decorView.findViewById<View>(DROP_DOWN_ID)?.let {
            decorView.removeView(it)
        }
        val composeView = ComposeView(activity).apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent{
                DropDownItems(modifier = Modifier.fillMaxWidth().wrapContentHeight(), list = list, selectIndex = selectIndex) {
                    decorView.removeView(this@apply)
                    onItemClick?.invoke(it)
                }
            }
            id = DROP_DOWN_ID
        }
        val lp = FrameLayout.LayoutParams(-1,-1)
        lp.topMargin = marginTopPx.toInt()
        decorView.addView(composeView,lp)
    }

    fun dismissDropDownView(activity: Activity){
        val decorView = activity.window.decorView as ViewGroup
        decorView.findViewById<View>(DROP_DOWN_ID)?.let {
            decorView.removeView(it)
        }
    }
}

级联选择器:它的承载容器是一个底部弹窗的 DialogFragment,显示的内容目前我用compose实现,已选的级联它横向显示采用的LazyRow,列表项使用LazyColumn实现。具体实现如下。

data class LevelsItem(
    val text:String,
    var list:List<LevelsItem>? = null,
    var parentId:String? = null,
    var level:Int? = null,
    val tag:Any? = null //LevelsItem,是从服务端给的数据模型转的,实际可以再转化的时候,将该tag    
                        //设置为服务端给的模型.如下我转换的代码
)
/**
    fun convertMotorcadeRespToLevelsItem(list: List<MotorcadeResp>): List<LevelsItem> {
        return list.map { motorcadeResp ->
            LevelsItem(
                text = motorcadeResp.name ?: "", // Map name to text
                parentId = motorcadeResp.parentId,
                level = motorcadeResp.departmentLevel,
                list = motorcadeResp.childDepartment?.let { convertMotorcadeRespToLevelsItem(it) }, // Recursively map childDepartment
                tag = motorcadeResp // Set the original MotorcadeResp as tag
            )
        }
    }
*/
/**
 * 级联选择,可以自行选择。自行点击确定,完成选择成功
 */
class LevelsSelector1Dialog : BottomDialogFragment() {
    private val SELECTSTR: String = "-10000000"

    var title: String? = null

    var list: List<LevelsItem> = ArrayList()

    var onConfirm: ((LevelsItem?) -> Unit)? = null

    //用来横向显示tab项列表的item
    private var currentSelectItem: SnapshotStateList<LevelsItem> = mutableStateListOf()
    //记录当前选择的值,最后用来点击确定,来回调给调用方使用的
    private var currentSelectItemVal: SnapshotStateList<LevelsItem> = mutableStateListOf()


    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                WelcomeAssistantTheme {
                    LevelsSelectorScreen()
                }
            }
        }
    }


    @Preview(widthDp = 375, heightDp = 812)
    @Composable
    private fun LevelsSelectorScreen() {
        currentSelectItem.clear()
        //横向tab,添加一个默认项 “请选择"
        currentSelectItem.add(LevelsItem(SELECTSTR, list))
        val height =
            (0.64f * requireContext().resources.displayMetrics.heightPixels) / LocalDensity.current.density

        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(height.dp)
                .background(
                    color = Color.White,
                    shape = RoundedCornerShape(
                        topStart = 24.dp,
                        topEnd = 24.dp,
                        bottomStart = 0.dp,
                        bottomEnd = 0.dp
                    )
                )
                .padding(16.dp)
        ) {
            Image(
                painterResource(R.mipmap.ic_levels_selector_close),
                modifier = Modifier
                    .clickable {
                        dismiss()
                    }
                    .size(24.dp)
                    .align(Alignment.TopEnd),
                contentDescription = null
            )
            Text(
                modifier = Modifier
                    .padding(top = 24.dp, bottom = 16.dp)
                    .align(Alignment.TopCenter),
                text = title ?: "",
                style = buildMSDBTextStyle(fontSize = 20.sp, color = ff0A1733)
            )
            val listItems = remember {
                mutableStateOf(list)
            }
            val currentSelectTabIndex = remember {
                mutableIntStateOf(0)
            }
            LazyRow(
                modifier = Modifier
                    .padding(top = 68.dp)
                    .fillMaxWidth()
                    .height(30.dp),
            ) {
                itemsIndexed(currentSelectItem) {index, selectItem ->
                    Column {
                        if (SELECTSTR == selectItem.text) {
                            Text(
                                modifier = Modifier.padding(end = 24.dp).singleClickable {
                                    if(currentSelectTabIndex.intValue == index){
                                        return@singleClickable
                                    }
                                    currentSelectTabIndex.intValue = index
                                    listItems.value =
                                        currentSelectItem[index - 1].list ?: ArrayList()
                                },
                                text = stringResource(R.string.levels_select),
                                style = buildMSDNTextStyle(color = FF9DA2AD, fontSize = 16.sp)
                            )
                        } else {
                            Text(
                                modifier = Modifier
                                    .padding(end = 24.dp)
                                    .widthIn(max = 110.dp)
                                    .singleClickable {
                                        if(currentSelectTabIndex.intValue == index){
                                            return@singleClickable
                                        }
                                        if (index >= 1) {
                                            listItems.value =
                                                currentSelectItem[index - 1].list ?: ArrayList()
                                        } else {
                                            listItems.value =
                                                currentSelectItem[currentSelectItem.size - 1].list
                                                    ?: ArrayList()
                                        }
                                        currentSelectTabIndex.intValue = index
                                    },
                                text = selectItem.text,
                                style = buildMSDBTextStyle(
                                    color = ff0A1733,
                                    fontSize = 16.sp,
                                ),
                                overflow = TextOverflow.Ellipsis,
                                maxLines = 1
                            )
                        }
                        if(currentSelectTabIndex.intValue == index) {
                            Box(
                                modifier = Modifier
                                    .align(Alignment.CenterHorizontally)
                                    .padding(end = 24.dp)
                                    .height(3.dp)
                                    .width(24.dp)
                                    .background(color = FF306DF4, shape = RoundedCornerShape(3.dp))
                            )
                        }
                    }
                }
            }

            LazyColumn(
                modifier = Modifier
                    .padding(top = 108.dp, bottom = 70.dp)
                    .fillMaxSize()
            ) {
                var currentLevelSelectItem: LevelsItem? = null
                itemsIndexed(listItems.value) {i, levelsItem ->
                    var isAlreadySelectItem = currentSelectItemVal.contains(levelsItem)
                    if (isAlreadySelectItem) {
                        currentLevelSelectItem = levelsItem
                    }
                    Row(modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 12.dp)
                        .singleClickable {
                            //子item会有很多,可以判断当前item的父id是否相同
                            val index = currentSelectItem.indexOfFirst {
                                it.parentId == levelsItem.parentId
                            }
                            //可选项是否还有子列表
                            if (levelsItem.list.isNullOrEmpty()) {
                                //级联最后的那个列表
                                if (currentLevelSelectItem != null) {
                                    currentSelectItemVal.removeRange(currentSelectItemVal.indexOf(currentLevelSelectItem),currentSelectItemVal.size)
                                }
                                isAlreadySelectItem = true
                                if(index > 0){
                                    currentSelectItem.removeAt(index)
                                }
                                currentSelectItemVal.add(levelsItem)
                            } else {
                                //可以点出下一个级联
                                if (index < 0) {
                                    currentSelectItem.add(
                                        currentSelectItem.size - 1,
                                        levelsItem
                                    )
                                } else {
                                    currentSelectItem.removeAt(index)
                                    currentSelectItem.add(index,levelsItem)
                                }
                                currentSelectTabIndex.intValue += 1
                                listItems.value = levelsItem.list ?: ArrayList()

                                if (!isAlreadySelectItem) {
                                    if (currentLevelSelectItem != null) {
                                        currentSelectItemVal.removeRange(
                                            currentSelectItemVal.indexOf(
                                                currentLevelSelectItem
                                            ), currentSelectItemVal.size
                                        )
                                    }
                                    currentSelectItemVal.add(levelsItem)
                                }
                            }
                        }) {

                        Row(modifier = Modifier.fillMaxWidth()) {
                            Text(
                                modifier = Modifier.weight(1f),
                                text = levelsItem.text,
                                style = buildMSDNTextStyle(
                                    color = if (isAlreadySelectItem) FF306DF4 else ff0A1733,
                                    fontSize = 16.sp
                                )
                            )
                            if (isAlreadySelectItem) {
                                Image(
                                    modifier = Modifier.size(24.dp),
                                    painter = painterResource(R.mipmap.ic_dialog_levels_check),
                                    contentDescription = null
                                )
                            }
                        }
                    }

                }
            }

            Spacer(
                modifier = Modifier
                    .align(alignment = Alignment.BottomCenter)
                    .padding(bottom = 69.5.dp)
                    .height(0.5.dp)
                    .fillMaxSize()
                    .background(color = FFF2F3F4)
            )
            Box(
                modifier = Modifier
                    .align(alignment = Alignment.BottomCenter)
                    .padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
                    .fillMaxWidth()
                    .height(40.dp)
                    .background(color = FF306DF4, RoundedCornerShape(24.dp))
                    .singleClickable {
                        if (currentSelectItemVal.size > 0) {
                            onConfirm?.invoke(currentSelectItemVal[currentSelectItemVal.size - 1])
                        } else {
                            onConfirm?.invoke(null)
                        }
                        dismiss()
                    },
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = stringResource(R.string.dialog_confirm_position_txt),
                    style = TextStyle(color = Color.White, fontSize = 14.sp)
                )
            }
        }

    }

}

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

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

相关文章

【每日八股】MySQL篇(一):概述

关系的三个范式是什么&#xff1f; 第一范式&#xff08;1NF&#xff09;&#xff1a;用来确保每列的原子性&#xff0c;要求每列都是不可再分的最小数据单元。 概括&#xff1a;表中的每一列都是不可分割的最小原子值&#xff0c;且每一行都是唯一的。 第二范式&#xff08…

Remainder Problem CF1207F

题目&#xff1a;题目链接 题目大意 题目描述 给你一个长度为 500000 的序列&#xff0c;初值为 0 &#xff0c;你要完成 q 次操作&#xff0c;操作有如下两种&#xff1a; 1 x y : 将下标为 x 的位置的值加上 y2 x y : 询问所有下标模 x 的结果为 y 的位置的值之和 输入格…

SpringBoot之自定义简单的注解和AOP

1.引入依赖 <!-- AOP依赖--> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.8</version> </dependency>2.自定义一个注解 package com.example.springbootdemo3.an…

自由学习记录(38)

python语法 def def print_receipt (store_name, items, total_price, cashier"Self-Checkout", payment_method"Credit Card"): Python 的 函数定义 语法 def print_receipt(...) → 定义了一个名为 print_receipt 的函数。store_name, items, total_…

【SQL实验】触发器

下载素材文件”tsgl”、“成绩管理”,将tsgl.bak和成绩管理.bak数据库还原到库中【导入操作在之前的文章中详细讲过】 触发器 1、为图书表设置更新触发器&#xff0c;根据总编号来更新书名、作者、出版社、分类号和单价(根据总编号找到相应记录&#xff0c;然后更新书名、作者…

CPU多级缓存机制

目录 一、前置知识 ---- CPU的核心 1.1. 单核与多核CPU 二、CPU多级缓存机制 三. 缓存的基本结构/缓存的存储结构 四、CPU缓存的运作流程/工作原理 五、CPU多级缓存机制的工作原理【简化版】 5.1. 缓存访问的过程 (5.1.1) L1缓存&#xff08;一级缓存&#xff09;访问 …

神经网络八股(3)

1.什么是梯度消失和梯度爆炸 梯度消失是指梯度在反向传播的过程中逐渐变小&#xff0c;最终趋近于零&#xff0c;这会导致靠前层的神经网络层权重参数更新缓慢&#xff0c;甚至不更新&#xff0c;学习不到有用的特征。 梯度爆炸是指梯度在方向传播过程中逐渐变大&#xff0c;…

SmartMediakit之音视频直播技术的极致体验与广泛应用

引言 在数字化时代&#xff0c;音视频直播技术已经深入到各个行业和领域&#xff0c;成为信息传递和交流的重要手段。视沃科技自2015年成立以来&#xff0c;一直致力于为传统行业提供极致体验的音视频直播技术解决方案&#xff0c;其旗下的大牛直播SDK凭借强大的功能和卓越的性…

【R包】tidyplots----取代ggplot2的科研绘图利器

文章目录 介绍安装Usage文档参考 介绍 tidyplots----取代ggplot2的科研绘图利器。tidyplots的目标是简化为科学论文准备出版的情节的创建。它允许使用一致和直观的语法逐渐添加&#xff0c;删除和调整情节组件。 安装 You can install the released version of tidyplots fro…

DeepSeek 15天指导手册——从入门到精通 PDF(附下载)

DeepSeek使用教程系列--DeepSeek 15天指导手册——从入门到精通pdf下载&#xff1a; https://pan.baidu.com/s/1PrIo0Xo0h5s6Plcc_smS8w?pwd1234 提取码: 1234 或 https://pan.quark.cn/s/2e8de75027d3 《DeepSeek 15天指导手册——从入门到精通》以系统化学习路径为核心&…

微信小程序实现拉卡拉支付

功能需求&#xff1a;拉卡拉支付&#xff08;通过跳转拉卡拉平台进行支付&#xff09;&#xff0c;他人支付&#xff08;通过链接进行平台跳转支付&#xff09; 1.支付操作 //支付 const onCanStartPay async (obj) > {uni.showLoading({mask: true})// 支付接口获取需要传…

Unity 第三人称人物切动画时人物莫名旋转

前提: 使用Starter Asset包中的第三人称插件包. 在给3D角色的动画器增加新动画时, 发现进入新动画会让角色莫名转动. 观察后发现是动画强行将朝向掰"正", 人物动画在进行时朝向会一直变化, 这使得动作非常的怪异. 对系动画进行以下处理后, 将可以解决这种不自然: 选…

启动Redis报错记录

突然启动Redis就报了个错&#xff1a;‘Could not create server TCP listening socket 127.0.0.1:6379: bind: 操作成功完成。‘ 查了下解决方案&#xff0c;应该是6379端口已绑定&#xff0c;服务没有关闭。 需要输入命令redis-cli 再输入shutdown 但又出现了新的问题&…

自然语言处理NLP 04案例——苏宁易购优质评论与差评分析

上一篇文章&#xff0c;我们爬取了苏宁易购平台某产品的优质评价和差评&#xff0c;今天我们对优质评价与差评进行分析 selenium爬取苏宁易购平台某产品的评论-CSDN博客 目录 1. 数据加载 2. 中文分词 3. 停用词处理 4. 数据标注与合并 5. 数据集划分 6. 文本特征提取 …

图片爬取案例

修改前的代码 但是总显示“失败” 原因是 修改之后的代码 import requests import os from urllib.parse import unquote# 原始URL url https://cn.bing.com/images/search?viewdetailV2&ccidTnImuvQ0&id5AE65CE4BE05EE7A79A73EEFA37578E87AE19421&thidOIP.TnI…

官方文档学习TArray容器

一.TArray中的元素相等 1.重载一下 元素中的 运算符&#xff0c;有时需要重载排序。接下来&#xff0c;我们将id 作为判断结构体的标识。 定义结构体 USTRUCT() struct FXGEqualStructInfo {GENERATED_USTRUCT_BODY() public:FXGEqualStructInfo(){};FXGEqualStructInfo(in…

Web刷题之PolarDN(中等)

1.到底给不给flag呢 代码审计 一道典型的php变量覆盖漏洞 相关知识 什么是变量覆盖漏洞 自定义的参数值替换原有变量值的情况称为变量覆盖漏洞 经常导致变量覆盖漏洞场景有&#xff1a;$$使用不当&#xff0c;extract()函数使用不当&#xff0c;parse_str()函数使用不当&…

学习笔记-250222

论文&#xff1a; Learning Hierarchical Prompt with Structured Linguistic Knowledge for Vision-Language Models 主要研究llm在图像分类中的能力&#xff0c;当提示输入目标类别时&#xff0c;llm能够生成相关的描述以及相应的结构化关系。 1.首先利用llm从普通的描述中获…

Unity游戏制作中的C#基础(1)界面操作基础

1.脚本有关注意事项 &#xff08;1&#xff09;.进入项目之后&#xff0c;一般创建一个文件夹Scripts用来存放c#脚本&#xff1b; &#xff08;2&#xff09;.在Scripts中创建脚本&#xff0c;双击脚本&#xff0c;进入VS编辑器&#xff0c;有如下结构&#xff1a; start&#…

为什么要将PDF转换为CSV?CSV是Excel吗?

在企业和数据管理的日常工作中&#xff0c;PDF文件和CSV文件承担着各自的任务。PDF通常用于传输和展示静态的文档&#xff0c;而CSV因其简洁、易操作的特性&#xff0c;广泛应用于数据存储和交换。如果需要从PDF中提取、分析或处理数据&#xff0c;转换为CSV格式可能是一个高效…