Android Jetpack Compose 实现一个电视剧选集界面

news2024/11/17 23:59:39

文章目录

  • 需求概述
  • 效果展示
  • 实现思路
  • 代码实现
  • 总结

需求概述

我们经常能看到爱奇艺或者腾讯视频这类的视频APP在看电视剧的时候都会有一个选集的功能。如下图所示

在这里插入图片描述这个功能其实很简单,就是绘制一些方块,在上面绘制上数字,还有标签啥的。当用户点击对应的数字式时可以切换到对应的剧集。如果剧集太多,屏幕展示不完,就可以滑动屏幕查看更多的剧集,就这么一个很简单的UI小组件。我们使用Compose来实现下。

效果展示

在这里插入图片描述

如上图所示,在UI的最上面是标题(选集),下面是我们绘制出的小方块。当小方块选中的时候会绘制一个指示器,如果剧集太多就需要能上滑展示更多的剧集

如果剧集少的时候,居左展示。如下
在这里插入图片描述
如果是横屏,则如下展示:
在这里插入图片描述

实现思路

可能很多读者会很容易的想到使用网格布局的控件LazyVerticalGrid实现,如果说不需要透明背景的话,这种方法是可行的,而且性能也会很好,但是如果要求背景可以设置透明度的话,这种方式就不行了,因为LazyVerticalGrid无法将背景设置成透明的,如我们的效果展示图中,可以看到我们的选集UI出现后,还可以看到后面的背景,如果使用LazyVerticalGrid,则无法实现这个效果。所以我们采用的方式是直接通过for循环绘制。使用两个for循环,分别负责绘制行和列,然后再处理点击的回调和选中的指示器就行了,我们可以使用Column,Box,Row,Text组件搭配使用,这些组件都是可以设置透明度的,能达到需求的效果。

代码实现

代码的实现很简单,就是一个composable函数,在代码中都做了注释,所以就不多废话了,原理也很简单,就是通过两个for循环分别绘制行和列,根据行和列之间的对应关系去计算显示的高度,padding等。

/**
 * @param row 需要展示的行数
 * @param col 需要展示的列数
 * @param contentPadding 内容的padding,默认15dp
 * @param displayRowCount 展示的函数,比如这个值为7,传入的row 为10,那么只会展示7行,多余的三行需要滑动查看,默认展示5行
 * @param numberPadding 数字方块之间的padding,默认11dp
 * @param contentTopPadding 内容顶部的padding,默认0dp
 * @param currentNum 当前选中的数字,需要根据它绘制指示器
 * @param isPortrait 是否是竖屏,需要根据横屏和竖屏来调整布局,默认我是竖屏
 * @param
 */

@Composable
fun ShowDramaSelectUI(
    row: Int,
    col: Int,
    contentPadding: Dp = 15.dp,
    displayRowCount: Int = 5,
    numberPadding: Dp = 11.dp,
    contentTopPadding: Dp = 0.dp,
    currentNum: Int = 1,
    isPortrait: Boolean = true,
    onNumSelect: (Int) -> Unit
) {
    // 记录滚动的状态
    val scrollState = rememberScrollState()
    val displayHeight =
        // 根据显示的行数计算容器的高度,下面的表达式不能换行,否则根据kotlin的语法特性,换行后的表达式不会参与计算
        // 这里的60dp是数字的方块的大小,也可通过传参数指定
        (displayRowCount) * (60.dp).value + (displayRowCount - 1) * numberPadding.value
    // 记录选中的数字
    var selectedNum by remember { mutableIntStateOf(currentNum) }
    Log.d(TAG, "walt: selectedNum====>: $selectedNum")

    //背景蒙层
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color(0xCC000000))
    )

    Box(
        modifier = Modifier
            .fillMaxSize()
            .padding(
                top = contentTopPadding,
                start = contentPadding,
                end = contentPadding
            ),
        contentAlignment = Alignment.TopCenter
    ) {
        Column(
            modifier = Modifier
                .wrapContentHeight()
                .wrapContentWidth(),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Box(
                modifier = Modifier.fillMaxWidth().padding(start = 10.dp),
                contentAlignment = Alignment.CenterStart
            ) {
                Text(
                    text = "选集", style = TextStyle(
                        color = Color(0xFF797F85),
                        fontSize = 14.sp,
                        fontWeight = FontWeight.Normal
                    )
                )
            } // 选集Box

            Spacer(modifier = Modifier.height(10.dp).fillMaxWidth())

            Column(
                modifier = Modifier
                    .height(displayHeight.dp)
                    .fillMaxWidth()
                    // 让控件拥有滑动的能力
                    .verticalScroll(scrollState),
                verticalArrangement = Arrangement.Top,
                // 根据横竖屏设置对齐方式
                horizontalAlignment = if ((isPortrait && (col < 5))
                    || (!isPortrait && (col < 10))
                ) {
                    Alignment.Start
                } else {
                    Alignment.CenterHorizontally
                }
            ) {
                // 绘制行
                for (i in 1..row) {
                    Row(
                        modifier = Modifier.wrapContentWidth().wrapContentHeight()
                    ) {
                        // 绘制列
                        for (j in col * (i - 1) + 1..(i - 1) * col + col) {
                            Box(
                                modifier = Modifier
                                    .size(60.dp)
                                    .clip(RoundedCornerShape(6.dp))
                                    .background(Color(0x8031373D))
                                    // 回调选中的数字
                                    .clickable {
                                        selectedNum = j
                                        onNumSelect(selectedNum)
                                    },
                                contentAlignment = Alignment.Center
                            ) {
                                Text(
                                    text = "$j",
                                    style = TextStyle(
                                        color = Color.White,
                                        fontSize = 20.sp,
                                        textAlign = TextAlign.Center
                                    ),
                                )

                                // 只有选中的数字和当前的数字相同时,才会展示指示器
                                if (j == selectedNum) {
                                    Box(
                                        modifier = Modifier.fillMaxSize(),
                                        contentAlignment = Alignment.BottomCenter
                                    ) {
                                        Divider(
                                            modifier = Modifier
                                                .fillMaxWidth(),
                                            thickness = 6.dp,
                                            color = Color(0xFF037FF5)
                                        )
                                    }
                                }
                            }

//                        // 绘制两个列之间的间距,如果不是最后一个item,才加Spacer
                            if ((j != (i - 1) * col + col)) {
                                Spacer(modifier = Modifier.height(60.dp).width(numberPadding))
                            }
                        }
                    } // Row

                    // 绘制行之前的间距
                    Spacer(modifier = Modifier.height(numberPadding).fillMaxWidth())
                }
            }
        }
    }
}

测试代码

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyComposeTheme {
                Box(modifier =
                Modifier
                    .fillMaxSize()
                ){
                    Image(painter = painterResource(R.drawable.m10),
                        modifier = Modifier.fillMaxSize(),
                        contentScale = ContentScale.Crop,
                        contentDescription = null)
                    ShowDramaSelectUI(row = 10, col = 10, isPortrait = true, onNumSelect = {
                            num->
                        Log.d(TAG,"$num has selected1 !!!")
                    })
                }
            }
        }
    }
}

总结

本文主要介绍的是一个剧集选集的功能,这里只是介绍了实现的方式,比较粗糙,读者可以按照自己的需求修改,有更好的实现方案也可以在评论区交流。本文主要起抛砖引玉的作用,也是记录自己实现的一个小需求。给需要的小伙伴打个样,欢迎交流指正。

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

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

相关文章

AI Pin 仅获1万订单,公司或10亿美元卖给惠普

Humane投入了五年时间研发一款旨在颠覆智能手机市场的设备&#xff0c;最终却可能以失败收场&#xff0c;该公司曾获OpenAI Sam Altman和Salesforce Marc Benioff的投资。 内容提要 1、Humane推出的AI Pin设备&#xff0c;近来遭到广泛批评&#xff0c;销量低迷。公司因此开始…

Navicat for MySQL 11软件下载及安装教程

软件简介&#xff1a; Navicat for SQL Server 是一套专为 SQL Server设计的全面的图形化数据库管理及开发工具&#xff0c;可进行创建、编辑和删除全部数据库对象&#xff0c;例如表、视图、函数、索引和触发器&#xff0c;或运行 SQL查询和脚本&#xff0c;查看或编辑 BLOBs…

双重循环、多重循环程序设计

双重循环格式&#xff1a; for(循环条件1){ 语句1&#xff1b; for(循环条件2){ 语句2&#xff1b; } } 例题1&#xff1a;输入一个整数n&#xff0c;输出一个n层的*三角形塔&#xff08;完成例1&#xff09;。 输入样例&#xff1a;6 输出样例&#xff1a; * ** *** **…

5G随身wifi上千元太贵了!高性价比5G随身wifi推荐,随身WiFi哪个牌子的最好用?

说到随身Wi-Fi&#xff0c;大家应该都不陌生。平时手机信号差网络卡顿&#xff0c;流量不够用&#xff0c;全靠随身wifi解决。现在&#xff0c;我们已经全面进入了5G时代&#xff0c;随身Wi-Fi也升级迭代&#xff0c;出现了支持5G的产品型号&#xff0c;网速更快体验感更好。但…

人工智能在【多模态:多组学+复发转移+肿瘤起源】的最新研究进展|顶刊速递·2024-06-11

小罗碎碎念 本期文献速递的主题是——人工智能多模态/多组学肿瘤的复发转移肿瘤起源。 今天的六篇文章比较特殊&#xff0c;大家要好好留心一下&#xff0c;因为选题是老板亲自下场定的&#xff0c;机会难得。 最近状态不太好&#xff0c;这周还要在北京待几天处理些事情&#…

C++入门 string常用接口(下)

目录 string类的常用接口说明 string类对象的修改操作&#xff08;修饰符&#xff09; operator & append & push_back assign & insert erase & replace swap & pop_back string类对象的非成员函数 operator relational operators&#xff08;关系…

02 DHCP原理与配置

目录 2.1 DHCP工作原理 1. 了解DHCP服务 2. 使用DHCP的好处 3. DHCP的分配方式 4. DHCP的租约过程 1. 客户机请求IP地址 2. 服务器响应 3. 客户机选择IP地址 4. 服务器确定租约 5. 重新登录 6. 更新租约 2.2 使用DHCP动态配置主机地址 2.2.1 配置DHCP服务器 1. 安装DHCP服务器…

java版UWB高精度实时定位系统源码springboot+vue

UWB人员定位系统&#xff0c;实现人员的自动识别、位置定位、区域报警等功能。该系统能高效记录人员信息&#xff0c;出入信息及位置信息&#xff0c;并能灵活的查询及管理历史轨迹&#xff0c;可极大提高信息安全度&#xff0c;有效弥补了视频监控的不足。使人员管理实现信息化…

万界星空科技SMT行业MES系统功能

在现代制造业中&#xff0c;SMT智能车间MES系统是一种全自动化的生产管理系统&#xff0c;用于监控和控制整个SMT生产流程。它通过监控SMT设备的运行状态、实时追踪生产数据&#xff0c;并与其他系统进行实时数据交换&#xff0c;以提高生产效率、降低生产成本。MES系统采用先进…

超详解——Python 序列详解——基础篇

目录 1. 序列的概念 字符串&#xff08;String&#xff09; 列表&#xff08;List&#xff09; 元组&#xff08;Tuple&#xff09; 2. 标准类型操作符 连接操作符&#xff08;&#xff09; 重复操作符&#xff08;*&#xff09; 索引操作符&#xff08;[]&#xff09; …

MySQL 8.0 安装、配置、启动、登录、连接、卸载教程

目录 前言1. 安装 MySQL 8.01.1 下载 MySQL 8.01.2 安装 MySQL 8.0 2. 配置 MySQL 8.02.1打开环境变量2.2新建变量 MYSQL_HOME2.3编辑 Path 变量 3. 启动MySQL 8.03.1验证安装与配置是否成功3.2初始化并注册MYSQL3.3 启动MYSQL服务 4.登录MySQL4.1修改账户默认密码4.2登录MYSQL…

6月11日 C++day6

#include <iostream>using namespace std; class Animal //讲解员 { public:Animal(){}virtual void perform(){cout << "" << endl;} }; class Lion:public Animal //狮子 { public:Lion(){}void perform(){Animal::perform();cout <<…

基于SSM+Jsp的交通事故档案管理系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

大模型高级 RAG 检索策略之混合检索

古人云&#xff1a;兼听则明&#xff0c;偏信则暗&#xff0c;意思是要同时听取各方面的意见&#xff0c;才能正确认识事物&#xff0c;只相信单方面的话&#xff0c;必然会犯片面性的错误。 在 RAG&#xff08;Retrieval Augmented Generation&#xff09;应用中也是如此&…

Generative AI原理本质、技术内核及工程实践之基于Vertex AI的大模型 (四)Vertex AI 如何将 LLM 提升到新水平

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…

[论文笔记]Query Rewriting for Retrieval-Augmented Large Language Models

引言 今天带来论文Query Rewriting for Retrieval-Augmented Large Language Models的笔记。 本篇工作从查询重写的角度介绍了一种新的框架&#xff0c;即重写-检索-阅读&#xff0c;而不是以前的检索-阅读方式&#xff0c;用于检索增强的LLM。关注的是搜索查询本身的适应性&…

服饰进口清关流程及注意事项 | 国际贸易数字化平台 | 箱讯科技

随着全球化进程的不断推进&#xff0c;我国消费者对国外品牌服饰的需求日益增长&#xff0c;衣服进口业务也随之蓬勃发展。作为一名从事进口衣服行业的专业人士&#xff0c;掌握清关流程及注意事项至关重要。本文将为您详细解析衣服进口清关流程&#xff0c;并提供一些实用建议…

【CHIP】LTC2991 读取温度电压电流 调试实例

文章目录 0. ENV1. LTC2991 数据说明1. 数据计算公式2. 寄存器概述1. 管脚使能寄存器2. 芯片使能寄存器 2. 软件实现1. 概述2. 源码(部分)3. 参考log 0. ENV 软件系统&#xff1a;略 LTC2991&#xff1a;VCC3.3 温度&#xff1a;温控接v1-v2 / v2-v3 / … (双端采样)电压&#…

怎么找回Excel表格密码,两个方法,轻松解密

有时候&#xff0c;打开很久以前的工作表&#xff0c;想要进行编辑&#xff0c;结果工作表设置了工作表保护&#xff0c;需要输入密码来撤消工作表保护&#xff0c;然而&#xff0c;自己又忘记了保护工作表的密码&#xff0c;怎么办&#xff1f; 1、使用备份文件 如果您有文件…

龙迅LT9211D MIPIDSI/CSI桥接到2 PORT LVDS,支持 3840x2160 30Hz分辨率

龙迅LT9211D描述&#xff1a; LT9211D是一款高性能的MIPI DSI/CSI-2到双端口LVDS转换器。LT9211D反序列化输入的MIPI视频数据&#xff0c;解码数据包&#xff0c;并将格式化的视频数据流转换为AP和移动显示面板或摄像机之间的LVDS发射机输出。LT9211D支持最大12.5 dB输入均衡和…