Android Compose UI实战练手----Google Bloom欢迎页

news2024/12/23 9:58:13

目录

  • 1.概述
  • 2.效果图展示
    • 2.1 亮色主题效果:
    • 2.2 深色主题效果
  • 3.项目结构解析
    • 3.1 颜色配置Color.kt
    • 3.2 形状配置Shape.kt
    • 3.3 主题配置Theme.kt
    • 3.4 字体配置 Type.kt
  • 4.沉浸式状态栏适配
  • 5.UI界面分解及实现
    • 5.1 欢迎页背景+内容
    • 5.2 欢迎页内容组件实现
  • 5.3 欢迎页内容的各个小组件实现
  • 6.代码地址

1.概述

在之前的博客中,我已经介绍了Compose 的基础UI和布局组件,现在我们就利用这些基础UI和布局组件去做一个实战项目。Bloom是Google提供的一个假想产品,我们可以作为练手项目使用,这个产品的详细UI设计稿大家可以自行去百度下,个人决定这里主要是熟练去使用Compose UI,不必要纠结设计稿,文末我会贴出项目的github地址,供读者参考。

2.效果图展示

在这里插入图片描述

2.1 亮色主题效果:

在这里插入图片描述

2.2 深色主题效果

在这里插入图片描述

3.项目结构解析

项目的大致结构就是一个简单的Android项目,如下图所示:

在这里插入图片描述

在上图中是这个项目的结构图,我们主要介绍的是ui下theme包类的几个Kotlin配置类,当我们创建一个新项目是,Compose会在项目中生产ui/theme目录,包含四个文件,分别是Color.kt、Shape.kt、Theme.kt、Type.kt;官方建议我没将颜色、字体、形状等配置信息放到这四个配置文件中,便于统一维护管理

3.1 颜色配置Color.kt

配置的颜色信息是一个四字节的Int整形数字,每个字节保存着ARGB对应的信息。例如Pink-100对应的值是0xFFF1F1,这里表示的只是低位RGB三种颜色对应的信息,如果希望添加透明度,那么就在最高位添加一个字节表示透明度,比如希望透明度是100%,那么就添加一个0xFF,最终的颜色值是0xFFFFF1F10xFF对应的十进制是255,表示100%的透明度,如果要表示85%的透明度,则255x85%对应的16进制是0xD8,所以0xFFFFF1F1对应的透明度为85%的颜色值为0xD8FFF1F1

在本项目中用到的颜色值定义如下:

val white = Color(0xFFFFFFFF)
val white150 = Color(0x26FFFFFF)
val white850 = Color(0xD9FFFFFF)
val pink100 = Color(0xFFFFF1F1)
val pink900 = Color(0xFF3F2C2C)
val green300 = Color(0xFFB8C9B8)
val green900 = Color(0xFF2D3B2D)

3.2 形状配置Shape.kt

如今的主流APP中我们都可以看到很多的UI控件都使用了圆角,圆角的大小在传统的View开发时通常都是在drawable中新建一个xml定义按钮的圆角样式,这样很不方便,因为如果我们只是想改变圆角的大小时,就需要我们再定义一个xml样式,但是使用Shape.kt后就不存在这个问题,因为圆角大小的配置放到了一个统一的地方。使用的时候直接引用就行了

本项目中的圆角大小定义:

val Shapes = Shapes(
    small = RoundedCornerShape(4.dp),
    medium = RoundedCornerShape(4.dp),
    large = RoundedCornerShape(0.dp)
)

3.3 主题配置Theme.kt

主题的配置相对要麻烦一些,由于篇幅原因,这里不多介绍,在后面会有专门的一篇博客对主题进行介绍,这里我们就先按照我提供的主题配置上就行了。本项目的主题配置如下:

private val BloomDarkColorPalette = darkColors(
    primary = white,
    secondary = green300,
    background = green900,
    surface = white150,
    onPrimary =  white850,
    onSecondary = green900,
    onBackground = pink900,
    onSurface = white850,
)

private val BloomLightColorPalette = lightColors(
    primary = pink900,
    secondary = pink900,
    background = pink100,
    surface = white850,
    onPrimary = pink100,
    onSecondary = pink100,
    onBackground = pink900,
    onSurface = white150
)

open class WelcomePageAssets(var background:Int,var illos:Int,var logo:Int)

//亮色主题资源
object LightWelcomeAssets : WelcomePageAssets(
    background = R.drawable.ic_light_welcome_bg,
    illos = R.drawable.ic_light_welcome_illos,
    logo = R.drawable.ic_light_logo
)

// 暗色主提资源

object DarkWelcomeAssets : WelcomePageAssets(
    background = R.drawable.ic_dark_welcome_bg,
    illos = R.drawable.ic_dark_welcome_illos,
    logo = R.drawable.ic_dark_logo
)

internal var LocalWelcomeAssets = staticCompositionLocalOf { LightWelcomeAssets as WelcomePageAssets }

val welcomeAssets
@Composable
@ReadOnlyComposable
get() = LocalWelcomeAssets.current

enum class BloomTheme{
    LIGHT,DARK
}

@Composable
fun GoogleBloomTheme(theme:BloomTheme = BloomTheme.LIGHT,content:@Composable ()->Unit){
    val welcomeAssets = if(theme == BloomTheme.DARK) DarkWelcomeAssets else LightWelcomeAssets
    CompositionLocalProvider(
        LocalWelcomeAssets provides welcomeAssets
    ) {
        MaterialTheme(colors = if (theme == BloomTheme.DARK)
            BloomDarkColorPalette else BloomLightColorPalette,
        typography = bloomTypoGraphy,
        shapes = shapes,
        content = content,
        )
    }
}

3.4 字体配置 Type.kt

我们想用的字体可以在Type.kt中配置,如果要引入新字体,可以将下载下来的字体文件放到res/font目录下,如果没有这个目录可以自己建一个,然后使用如下的方式引入字体


val nunitoSansFamily = FontFamily(
    Font(R.font.nunitosans_light, FontWeight.Light),
    Font(R.font.nunitosans_semibold, FontWeight.SemiBold),
    Font(R.font.nunitosans_bold, FontWeight.Bold)
)

本项目的字体配置

// Set of Material typography styles to start with
val Typography = Typography(
    body1 = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp
    )
)

val nunitoSansFamily = FontFamily(
    Font(R.font.nunitosans_light, FontWeight.Light),
    Font(R.font.nunitosans_semibold, FontWeight.SemiBold),
    Font(R.font.nunitosans_bold, FontWeight.Bold)
)

val bloomTypoGraphy = Typography(
    h1 = TextStyle(
        fontSize = 18.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Bold
    ),
    h2 = TextStyle(
        fontSize = 14.sp,
        letterSpacing = 0.15.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Bold
    ),
    subtitle1 = TextStyle(
        fontSize = 16.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Light
    ),
    body1 = TextStyle(
        fontSize = 14.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Light
    ),
    body2 = TextStyle(
        fontSize = 12.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.Light
    ),
    button = TextStyle(
        fontSize = 14.sp,
        letterSpacing = 1.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.SemiBold,
        color = white
    ),
    caption = TextStyle(
        fontSize = 12.sp,
        fontFamily = nunitoSansFamily,
        fontWeight = FontWeight.SemiBold
    )
)

4.沉浸式状态栏适配

所谓沉浸式主题栏适配就是指手机状态栏的颜色和我们的应用背景色相同,看起来感觉状态栏和我们的页面就像是一体的一样,有的做法是给标题栏设置一个透明的颜色,而本项目中使用的办法是将标题栏的颜色设置成和页面的背景色相同就可以了。设置标题栏的颜色需要用到一个库:

implementation "com.google.accompanist:accompanist-systemuicontroller:0.31.0-alpha"

引入这个库后,使用下面的方法设置状态栏的颜色:

    @Composable
    fun TransparentSystemBars(color: Color) {
        val systemUiController = rememberSystemUiController()
        val useDarkIcons = !isSystemInDarkTheme()
        SideEffect {
            systemUiController.setSystemBarsColor(
                color = color,
                darkIcons = useDarkIcons,
                isNavigationBarContrastEnforced = false,
            )
        }
    }

然后在对应的Activity中设置好主题,代码如下:

class MainActivity : ComponentActivity() {
    private lateinit var theme: BloomTheme
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        actionBar?.apply { hide() }
        setContent {
            theme = if(isSystemInDarkTheme()){
                BloomTheme.DARK
            }else {
                BloomTheme.LIGHT
            }
            
            GoogleBloomTheme(theme) {
                val color = MaterialTheme.colors.background
                TransparentSystemBars(color)
                // 展示界面,例如本项目中展示欢迎页:WelcomePage()
                
            }
        }
    }
}

如上面代码所示,我们根据当前的系统是否是深色主题判断使用的主题样式,然后把应用的当前的背景色传给设置状态栏颜色的方法,其他页面也是相同的做法。

5.UI界面分解及实现

首先我们可以先看下欢迎页:
在这里插入图片描述
我们可以将这个页面看成是背景加上内容,然后我们对显示的内容划分下,如下所示:
在这里插入图片描述
根据前面所学的知识,我们可以使用一个Column组件搞定这个页面的内容布局。而背景和内容的结合,我们可以使用Box组件,代码如下:

5.1 欢迎页背景+内容

使用Box组件将内容和背景融合到一起,内容我们可以抽成一个组件继续去划分实现

@Composable
fun WelcomePage(){
    Box(modifier = Modifier
        .fillMaxSize()
        .background(MaterialTheme.colors.background)
    ){
        Image(
            painter = rememberVectorPainter(
                image = ImageVector.vectorResource(id = welcomeAssets.background)
            ),
            contentDescription = "welcome page",
            modifier = Modifier.fillMaxSize()
        )

        WelcomeContent()
    }
}

5.2 欢迎页内容组件实现

内容组件又包括了一张叶子样式的图片,标题和两个按钮,我们可以分别抽成组件

@Composable
fun WelcomeContent(){
    Column(modifier = Modifier.fillMaxSize()) {
        Spacer(modifier = Modifier.height(72.dp))
        LeafImage()
        Spacer(modifier = Modifier.height(48.dp))
        WelcomeTitle()
        Spacer(modifier = Modifier.height(40.dp))
        WelcomeButtons()
    }
}

5.3 欢迎页内容的各个小组件实现

上面的WelcomeContent()函数将要显示的内容拆分成了三个更小的组件,实现如下所示:

@Composable
fun LeafImage(){
    Image(painter = rememberVectorPainter(
        image = ImageVector.vectorResource(id = welcomeAssets.illos)
    ),
        contentDescription = "welcome leaf image",
    modifier = Modifier
        .wrapContentSize()
        .padding(start = 88.dp))
}

@Composable
fun WelcomeTitle(){
    Column(horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxWidth()) {
        Image(painter = rememberVectorPainter(
            image = ImageVector.vectorResource(id = welcomeAssets.logo)),
            contentDescription = "welcome logo",
        modifier = Modifier
            .wrapContentSize()
            .height(32.dp))

        Box(modifier = Modifier
            .fillMaxWidth()
            .height(32.dp),
            contentAlignment = Alignment.BottomCenter){
            Text(text = "Beautiful home garden solutions",
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.subtitle1,
            color = MaterialTheme.colors.primary)
        }
    }
}

@Composable
fun WelcomeButtons(){
    Column(horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxWidth()) {
        Button(onClick = { /*TODO*/ },
        modifier = Modifier
            .height(48.dp)
            .padding(horizontal = 16.dp)
            .fillMaxWidth()
            .clip(MaterialTheme.shapes.medium),
            colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.secondary)
        ) {
            Text(
                text = "Create account",
                style = MaterialTheme.typography.button,
                color = MaterialTheme.colors.onSecondary
            )
        }

        Spacer(modifier = Modifier.height(24.dp))
        TextButton(onClick = { /*TODO*/ }) {
            Text(
                text = "Log in",
                style = MaterialTheme.typography.button,
                color = MaterialTheme.colors.primary            )
        }
    }
}

至此,一个使用Compose实现的欢迎页就完成了。是不是很简单,代码也很少。

6.代码地址

建议读者将源码下载下来,跟着敲一遍,不懂的可以查官网,百度下,主要是为了熟悉如何使用Compose的开发。有问题也可以在评论区一起交流。
源码地址

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

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

相关文章

自动化测试成熟度模型

目录 前言: 重新认识自动化测试 新手落地自动化测试 自动化测试成熟度模型 初级阶段-测试半自动化 中级阶段-回归测试自动化 高级阶段-大范围自动化测试 成熟阶段-自动化测试流水线 前言: 随着软件行业的快速发展,软件质量已经成为各…

chatgpt赋能python:Python如何绘制坐标轴

Python如何绘制坐标轴 在数据可视化中,坐标轴是一种非常常见的图形元素,其作用是让人们更好地理解和分析数据。在Python编程中,我们可以使用各种数据可视化库如Matplotlib和Seaborn来绘制坐标轴。本文将介绍如何使用Matplotlib库来绘制坐标轴…

17. 数据结构之图

前言 前面介绍了队列,栈等线性数据结构,二叉树,AVL树等非线性数据结构,本节,我们介绍一种新的非线性数据结构:图。图这种结构有很广泛的应用,比如社交网络,电子地图,多对…

【JVM篇】Java内存区域与OOM

目录 1、概述 2、运行时数据区域 3、程序计数器 4、Java虚拟机栈 5、本地方法栈 6、Java堆 7、方法区 8、运行时常量池 9、直接内存 1、概述 内存是非常重要的系统资源,是硬盘和 CPU 的中间仓库及桥梁,承载着操作系统和应用程序的实时运行。JVM…

Windows Server 2025预览版安装配置

一、安装篇 1.2.目前最新版是 25379版本,需要注册微软账户,加入先期预览计划才可以下载。 下载完镜像后,制作U盘启动盘,安装。 注意:无论是rufs制作启动盘,还是在安装的时候,都要使用UEFI模式…

Spring boot开发微信小游戏后台-websocket服务

最近在做一个微信小游戏的后台,需要使用websocket与小游戏端建立连接,实时推送数据,小游戏后台是一个单体spring boot项目,管理玩家的一些基础信息和游戏配置信息,起初在这个单体项目中加入了websocket,可以…

spring boot+easyui粮油质量管控防伪溯源系统源码

基于物联网技术、RFID技术和RSA、PGP加密算法开发的粮油质量追溯系统 粮油安全关系千千万万消费者的健康问题。近年来,许多食品行业安全事故频频涌现,成为社会关注焦点。粮油生产加工质量管控防伪溯源系统为粮油提供从种植、生产、加工、销售等各环节的…

Linux下实现自己的printf函数

Linux下实现自己的printf函数 文章目录 Linux下实现自己的printf函数项目中的使用实现自己的printf函数 项目中的使用 /*********************************************************************** 函数名称: DebugPrint* 功能描述: 打印信息的总入口函…

Nodejs四、npm与包

零、文章目录 Nodejs四、npm与包 1、包 (1)包是什么 Node.js 中的第三方模块又叫做包。就像电脑和计算机指的是相同的东西,第三方模块和包指的是同一个概念,只不过叫法不同。 (2)包的来源 不同于 Nod…

Linux SSH PublicKey 登录

前言 ssh 远程登录密码认证的方式有 Password、Keyboard Interactive 和 Public Key 三种主要方式。 前面两种方式就是密码认证,含义都是一样大同小异。第三种是登录方式最安全的一种,也是我们常用的云服务器默认使用的一种方式。 本文就如何配置并使用…

torchvision.ops.nms实现NMS

nms原理&#xff1a; 当目标检测模型对一个目标有多个检测框时&#xff0c;需要滤掉多余的框&#xff0c;留下最接近真实目标的框。 步骤是这样的&#xff1a; 1.先把目标框初筛一波&#xff0c;比如设阈值为0.25, 把预测概率 < 0.25的目标框滤掉。 2.把 每个类别的 目标框 …

DEVONthink 3:Mac文档管理工具,知识管理app

DEVONthink Pro是一款功能强大的文档管理软件&#xff0c;它可以帮助用户高效地组织、管理和查找各种类型的文件和信息。 下面是DEVONthink Pro的主要特点介绍&#xff1a; 多功能性&#xff1a;DEVONthink Pro支持多种文件类型和数据源&#xff0c;并提供全面的搜索、分类、过…

在 ZBrush、Substance 3D Painter 和 UE5 中创作警探角色(P1)

小伙伴们大家好&#xff0c;今天瑞云渲染小编给大家分享的是自由CG艺术家Jean Zoudi创建《极乐迪斯科》的警探角色的项目花絮&#xff0c;会解释身体和服装的建模方式&#xff0c;分享角色发型和面部毛发背后的工作流程&#xff0c;也会详细介绍渲染过程。 介绍 大家好&#…

性能测试怎么做?性能测试策略配套适用场景,打通性能测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、常见的测试策略…

直流对数放大器

Logarithmic Amplifiers 对数放大器的应用场合 在雷达和一些其他测距的场合&#xff0c;sensor输出的信号的动态范围比较宽&#xff0c;也就是要求sensor输出的弱信号时有比较大的放大倍数&#xff0c;强的信号有较小的放大倍数&#xff0c;以保证sensor输出的信号经过放大器后…

可移动硬盘无媒体是什么意思?移动硬盘显示无媒体数据如何恢复

案例分享&#xff1a;【最近我遇到了一个麻烦&#xff0c;我的移动硬盘突然显示“无媒体”。我不知道发生了什么&#xff0c;我很担心我的硬盘中存储的大量重要数据是否还能恢复。我该怎么解决移动硬盘显示无媒体问题呢&#xff0c;求大神帮帮我吧&#xff01;&#xff01;&…

浏览器是如何实现生成HTTP消息的

我们经常会使用浏览器访问各种网站&#xff0c;获取各种信息&#xff0c;帮助解决工作生活中的问题。那你知道&#xff0c;浏览器是怎么帮助我们实现对web服务器的访问&#xff0c;并返回给我们想要的信息吗&#xff1f; 1. 浏览器生成HTTP消息 我们平时使用的浏览器有很多种&…

【强烈推荐】 十多款2023年必备国内外王炸级AI工具 (免费 精品 好用) 让你秒变神一样的装逼佬感受10倍生产力 (6) AI学习

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

【LLMs系列】90%chatgpt性能的小羊驼Vicuna模型学习与实战

一、前言 UC伯克利学者联手CMU、斯坦福等&#xff0c;再次推出一个全新模型70亿/130亿参数的Vicuna&#xff0c;俗称「小羊驼」&#xff08;骆马&#xff09;。小羊驼号称能达到GPT-4的90%性能 github 地址: GitHub - lm-sys/FastChat: An open platform for training, servi…

ChatGPT爆火网络背后的故事?

文章目录 前言一、ChatGPT的诞生背景二、ChatGPT的技术原理三、ChatGPT的推广策略四、ChatGPT的未来展望五、橙子送书第2期 前言 ChatGPT是一款基于人工智能技术的聊天机器人&#xff0c;它的出现引起了广泛的关注和热议。在短短的时间内&#xff0c;ChatGPT就成为了全球范围内…