Android开发实战班 - 现代 UI 开发之自定义 Compose 组件
Jetpack Compose 不仅提供了丰富的内置组件,还允许开发者根据项目需求创建自定义组件。自定义 Compose 组件可以提高代码复用性、简化 UI 逻辑,并使应用界面更加一致和模块化。本章节将介绍如何创建自定义 Compose 组件,包括组件的创建、样式定制、状态管理以及在项目中的应用。
为什么需要自定义 Compose 组件
-
代码复用:
- 通过创建自定义组件,可以将常用的 UI 逻辑封装起来,避免重复代码,提高代码复用性。
-
简化 UI 逻辑:
- 将复杂的 UI 逻辑封装到自定义组件中,可以简化调用代码,使代码更简洁易懂。
-
一致性:
- 自定义组件可以保证应用界面的一致性,例如统一的按钮样式、输入框样式等。
-
模块化:
- 自定义组件可以将 UI 拆分成更小的模块,便于维护和测试。
创建自定义 Compose 组件
创建自定义 Compose 组件非常简单,只需定义一个带有 @Composable
注解的函数即可。以下是一个简单的自定义按钮组件示例:
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp@Composable
fun MyButton(text: String,onClick: () -> Unit,modifier: Modifier = Modifier,backgroundColor: Color = MaterialTheme.colorScheme.primary,contentColor: Color = MaterialTheme.colorScheme.onPrimary
) {Text(text = text,modifier = modifier.clip(RoundedCornerShape(8.dp)).background(backgroundColor).clickable { onClick() }.padding(vertical = 12.dp, horizontal = 16.dp),color = contentColor,style = MaterialTheme.typography.labelLarge)
}
-
参数说明:
text
: 按钮显示的文本。onClick
: 按钮点击事件回调。modifier
: 修饰符,用于自定义按钮的布局和样式。backgroundColor
: 按钮背景颜色,默认为主题主色。contentColor
: 按钮文本颜色,默认为主题主色文本颜色。
-
使用自定义按钮:
MyButton(text = "Click Me",onClick = { /* 处理点击事件 */ },modifier = Modifier.align(Alignment.Center) )
样式定制
自定义 Compose 组件可以通过参数传递样式,例如颜色、字体、形状等,从而实现样式的定制。
-
颜色定制:
- 可以通过参数传递不同的颜色值来定制组件的颜色。
MyButton(text = "Red Button",onClick = { /* 处理点击事件 */ },backgroundColor = Color.Red,contentColor = Color.White )
- 可以通过参数传递不同的颜色值来定制组件的颜色。
-
字体和排版:
- 可以通过
MaterialTheme.typography
来定制组件的字体和排版。MyButton(text = "Large Button",onClick = { /* 处理点击事件 */ },modifier = Modifier.padding(16.dp),style = MaterialTheme.typography.headlineMedium )
- 可以通过
-
形状定制:
- 可以通过
clip()
和RoundedCornerShape
等函数来定制组件的形状。MyButton(text = "Rounded Button",onClick = { /* 处理点击事件 */ },modifier = Modifier.clip(RoundedCornerShape(16.dp)) )
- 可以通过
状态管理
自定义 Compose 组件可以管理自己的状态,例如按钮的选中状态、输入框的内容等。
-
按钮选中状态:
@Composable fun ToggleButton(text: String,isSelected: Boolean,onToggle: (Boolean) -> Unit,modifier: Modifier = Modifier ) {val backgroundColor = if (isSelected) MaterialTheme.colorScheme.primary else Color.Grayval contentColor = if (isSelected) MaterialTheme.colorScheme.onPrimary else Color.WhiteText(text = text,modifier = modifier.clip(RoundedCornerShape(8.dp)).background(backgroundColor).clickable { onToggle(!isSelected) }.padding(vertical = 12.dp, horizontal = 16.dp),color = contentColor,style = MaterialTheme.typography.labelLarge) }// 使用 ToggleButton var isButtonSelected by remember { mutableStateOf(false) }ToggleButton(text = "Toggle Me",isSelected = isButtonSelected,onToggle = { isButtonSelected = it } )
-
输入框内容状态:
@Composable fun MyTextField(value: String,onValueChange: (String) -> Unit,modifier: Modifier = Modifier,label: String = "" ) {TextField(value = value,onValueChange = onValueChange,label = { Text(text = label) },modifier = modifier,colors = TextFieldDefaults.textFieldColors(containerColor = MaterialTheme.colorScheme.secondaryContainer,textColor = MaterialTheme.colorScheme.onSecondaryContainer,placeholderColor = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.5f))) }// 使用 MyTextField var text by remember { mutableStateOf("") }MyTextField(value = text,onValueChange = { text = it },label = "Enter text" )
实战案例
-
案例一:自定义导航栏组件
- 创建一个自定义导航栏组件,包含多个导航项。
- 使用
Row
和Button
组件实现导航栏布局。 - 通过参数传递导航项列表和点击事件回调。
@Composable fun MyNavigationBar(items: List<String>,onItemClick: (String) -> Unit,modifier: Modifier = Modifier ) {Row(modifier = modifier) {items.forEach { item ->MyButton(text = item,onClick = { onItemClick(item) },modifier = Modifier.weight(1f))}} }// 使用 MyNavigationBar val navigationItems = listOf("Home", "Settings", "Profile") var selectedItem by remember { mutableStateOf("Home") }MyNavigationBar(items = navigationItems,onItemClick = { selectedItem = it } )
-
自定义卡片组件
- 创建一个自定义卡片组件,包含标题、内容和按钮。
- 使用
Card
,Column
,Text
,Button
等组件实现卡片布局。 - 通过参数传递标题、内容和按钮文本。
@Composable fun MyCard(title: String,content: String,buttonText: String,onButtonClick: () -> Unit,modifier: Modifier = Modifier ) {Card(modifier = modifier,elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),shape = RoundedCornerShape(8.dp)) {Column(modifier = Modifier.padding(16.dp)) {Text(text = title,style = MaterialTheme.typography.titleMedium,modifier = Modifier.padding(bottom = 8.dp))Text(text = content,style = MaterialTheme.typography.bodyMedium,modifier = Modifier.padding(bottom = 12.dp))MyButton(text = buttonText,onClick = onButtonClick)}} }// 使用 MyCard MyCard(title = "Welcome",content = "Thank you for using our app!",buttonText = "OK",onButtonClick = { /* 处理按钮点击事件 */ } )
课后作业
-
任务一:创建一个自定义按钮组件
- 定义一个自定义按钮组件
MyButton
,包含文本、点击事件、颜色、字体等参数。 - 使用
Modifier
和 Compose 布局组件实现按钮布局。 - 在应用中调用
MyButton
组件,并传递不同的参数。
- 定义一个自定义按钮组件
-
任务二:创建一个自定义卡片组件
- 定义一个自定义卡片组件
MyCard
,包含标题、内容、按钮文本和点击事件等参数。 - 使用
Card
,Column
,Text
,Button
等组件实现卡片布局。 - 在应用中调用
MyCard
组件,并传递不同的参数。
- 定义一个自定义卡片组件
-
任务三:实现一个自定义导航栏组件
- 定义一个自定义导航栏组件
MyNavigationBar
,包含导航项列表和点击事件回调。 - 使用
Row
和MyButton
组件实现导航栏布局。 - 在应用中调用
MyNavigationBar
组件,并传递导航项列表和点击事件回调。
- 定义一个自定义导航栏组件
通过本章节的学习,学员将能够掌握创建自定义 Compose 组件的方法,包括组件的创建、样式定制、状态管理以及在项目中的应用,从而提高代码复用性、简化 UI 逻辑,并使应用界面更加一致和模块化。
作者简介
前腾讯电子签的前端负责人,现 whentimes tech CTO,专注于前端技术的大咖一枚!一路走来,从小屏到大屏,从 Web 到移动,什么前端难题都见过。热衷于用技术打磨产品,带领团队把复杂的事情做到极简,体验做到极致。喜欢探索新技术,也爱分享一些实战经验,帮助大家少走弯路!
温馨提示:可搜老码小张公号联系导师
相关文章:
Android开发实战班 - 现代 UI 开发之自定义 Compose 组件
Jetpack Compose 不仅提供了丰富的内置组件,还允许开发者根据项目需求创建自定义组件。自定义 Compose 组件可以提高代码复用性、简化 UI 逻辑,并使应用界面更加一致和模块化。本章节将介绍如何创建自定义 Compose 组件,包括组件的创建、样式…...

All-in-one Notion 介绍
Notion 是一款集笔记、知识管理、任务规划和协作于一体的工具,它以高度的灵活性和可自定义的工作空间而闻名。它适合个人和团队使用,能够帮助用户高效管理生活、学习和工作。以下是 Notion 的一些主要特点: 1. 多功能工作区 Notion 将笔记、…...

深入理解C++11右值引用与移动语义:高效编程的基石
文章目录 前言📚一、C11的历史发展📖1.1 C11 之前的背景📖1.2 C11 的发展历程📖1.3 C11 的主要设计目标📖1.4 C11 的主要特性📖1.5 C11 的影响 📚二、统一的列表初始化📖2.1 基本列表…...

【WRF-Urban】URBPARM_LCZ.TBL 查找表解释及内容
【WRF-Urban】URBPARM_LCZ.TBL 查找表解释及内容 URBPARM_LCZ.TBL 文件的作用URBPARM_LCZ.TBL 文件中的参数URBPARM_LCZ.TBL 的使用URBPARM_LCZ.TBL 文件内容如何调整或扩展 URBPARM_LCZ.TBL参考URBPARM_LCZ.TBL 文件是 WRF(天气研究与预报模型) 中用于处理 局地气候区(Loca…...
网络是怎么连接的
目录 URL HTTP(超文本传输协议) FTP(文件传输协议) File mailto HTTP的方法 Socket URL URL(Uniform Resource Locator)中文名为统一资源定位符,它是互联网上用来标识某一资源的地址。就…...
Java 实现PDF添加水印
maven依赖: <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.4.3</version> </dependency>网络地址添加水印代码: public static boolean waterMarkNet(Stri…...

网络安全问题概述
1.1.计算机网络面临的安全性威胁 计算机网络上的通信面临以下的四种威胁: (1) 截获——从网络上窃听他人的通信内容。 (2) 中断——有意中断他人在网络上的通信。 (3) 篡改——故意篡改网络上传送的报文。可应用于域名重定向,即钓鱼网站。 (4) 伪造——伪…...

(udp)网络编程套接字Linux(整理)
源IP地址和目的IP地址 唐僧例子1 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址.思考: 我们光有IP地址就可以完成通信了嘛? 想象一下发qq消息的例子, 有了IP地址能够把消息发送到对方的机器上,但是还需要有一个其他的标识来区分出, 这个数据要给哪个程序进…...
Web应用安全入门:架构搭建、漏洞分析与HTTP数据包处理
Web应用安全入门:架构搭建、漏洞分析与HTTP数据包处理 引言 在当今数字化时代,Web应用已成为企业和个人在线交互的核心。然而,随着技术的发展,Web应用面临的安全挑战也日益增加。本文旨在为初学者提供一个关于Web应用架构搭建、…...

[JAVA]MyBatis框架—获取SqlSession对象
SqlSessionFactory作为MyBatis框架的核心接口有三大特性 SqlSessionFactory是MyBatis的核心对象 用于初始化MyBatis,创建SqlSession对象 保证SqlSessionFactory在应用中全局唯一 1.SqlSessionFactory是MyBatis的核心对象 假设我们要查询数据库的用户信息&#x…...
Perl 简介
Perl 简介 Perl 是一种高级、通用、解释型、动态编程语言。由 Larry Wall 于 1987 年首次发布,它结合了 C、sed、awk 和 shell 脚本语言的特性。Perl 最初被设计用于文本处理,如报告生成和文件转换,但随着时间的推移,它已经发展成…...
spring-bean的销毁流程
1 引入 在 Spring 框架中,Bean 的生命周期管理是其核心功能之一,而 Bean 的注销(Destruction)是生命周期的最后一步。无论是关闭数据库连接、释放线程资源,还是执行缓存持久化操作,合适的销毁策略都至关重…...

问:Spring MVC DispatcherServlet流程步骤梳理
DispatcherServlet是Spring MVC框架中的核心组件,负责接收客户端请求并将其分发到相应的控制器进行处理。作为前端控制器(Front Controller)的实现,DispatcherServlet在整个请求处理流程中扮演着至关重要的角色。本文将探讨Dispat…...

用源码编译虚幻引擎,并打包到安卓平台
用源码编译虚幻引擎,并打包到安卓平台 前往我的博客,获取更优的阅读体验 作业内容: 源码编译UE5.4构建C项目,简单设置打包到安卓平台 编译虚幻 5 前置内容 这里需要将 Epic 账号和 Github 账号绑定,然后加入 Epic 邀请的组织,…...

快速搭建Android开发环境:Docker部署docker-android并实现远程连接
目录 前言 1. 虚拟化环境检查 2. Android 模拟器部署 3. Ubuntu安装Cpolar 4. 配置公网地址 5. 远程访问 小结 6. 固定Cpolar公网地址 7. 固定地址访问 作者简介: 懒大王敲代码,计算机专业应届生 今天给大家聊聊快速搭建Android开发环境&#x…...
「Mac玩转仓颉内测版21」基础篇1 - 仓颉程序的基本组成
本篇将系统介绍Cangjie编程语言中程序的基本组成部分,涵盖 main 函数的定义、包与模块的使用、变量类型、作用域和代码结构原则,帮助开发者理解Cangjie程序的整体结构。 关键词 程序入口点main函数包与模块变量类型与作用域值类型与引用类型代码结构与规…...

【Linux网络编程】简单的UDP套接字
目录 一,socket编程的相关说明 1-1,sockaddr结构体 1-2,Socket API 二,基于Udp协议的简单通信 三,UDP套接字的应用 3-1,实现英译汉字典 一,socket编程的相关说明 Socket编程是一种网络通信…...

在Vue中使用Excalidraw实现在线画板
概述 Excalidraw是一个非常好用的画图板工具,但是是用React写的,本文分享一种在Vue项目中使用的方法。 效果 实现 Excalidraw简介 这篇文章(Excalidraw 完美的绘图工具:https://zhuanlan.zhihu.com/p/684940131)介绍的很全面,…...

游戏+AI的发展历程,AI技术在游戏行业的应用有哪些?
人工智能(AI)与游戏的结合,不仅是技术进步的体现,更是人类智慧的延伸。从最初的简单规则到如今的复杂决策系统,AI在游戏领域的发展历史可谓波澜壮阔。 早在2001年,就有研究指出游戏人工智能领域࿰…...

Methode Electronics EDI 需求分析
Methode Electronics 是一家总部位于美国的全球性技术公司,专注于设计和制造用于多个行业的电子和电气组件,产品涵盖汽车、工业、电信、医疗设备以及消费电子等多个领域,提供创新的解决方案。 填写Methode_EDI_Parameters_Template Methode_…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
[USACO23FEB] Bakery S
题目描述 Bessie 开了一家面包店! 在她的面包店里,Bessie 有一个烤箱,可以在 t C t_C tC 的时间内生产一块饼干或在 t M t_M tM 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC,tM≤109)。由于空间…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...
boost::filesystem::path文件路径使用详解和示例
boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类,封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解,包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...

Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...
【Java基础】向上转型(Upcasting)和向下转型(Downcasting)
在面向对象编程中,转型(Casting) 是指改变对象的引用类型,主要涉及 继承关系 和 多态。 向上转型(Upcasting) ⬆️ 定义 将 子类对象 赋值给 父类引用(自动完成,无需强制转换&…...