2024年已经过半了,我作为聋人独立开发者,我经常会时不时反思:自己这半年到底进步了多少?在这篇文章里,我分享一个用 Jetpack Compose、Material3和 Kotlin 语言实现登录页面的案例。如果你有一定开发经验,相信这篇文章对你会非常有所帮助。
Material 3 是 Google 的最新 UI 框架,以声明式 UI 构建方式取代了传统的 XML 布局,很大提升了编程效率,减少了许多繁琐的代码。本项目使用 Compose、Material 3和 Kotlin 语言,实现一个登录页面。
PS:适合已有编程基础的开发者,如果你是初学者,建议先看看我另一篇基础文章:安卓软件开发:用Java和Kotlin构建MDC-UI框架实现LoginUI(基础)-腾讯云开发者社区-腾讯云 (tencent.com)
登录页面的基本结构:
• 两个文本字段:用于输入用户名和密码。
• 两个按钮:分别为“Cancel”按钮和“Next”按钮。
在项目的 build.gradle 文件中添加 Compose 和 Material 3 的依赖项:
dependencies {
implementation libs.androidx.core.ktx
implementation libs.androidx.lifecycle.runtime.ktx
implementation libs.androidx.activity.compose
implementation platform(libs.androidx.compose.bom)
implementation libs.androidx.ui
implementation libs.androidx.ui.graphics
implementation libs.androidx.ui.tooling.preview
implementation libs.androidx.material3
testImplementation libs.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
androidTestImplementation platform(libs.androidx.compose.bom)
androidTestImplementation libs.androidx.ui.test.junit4
debugImplementation libs.androidx.ui.tooling
debugImplementation libs.androidx.ui.test.manifest
}
[versions]
agp = "8.6.0"
kotlin = "1.9.0"
coreKtx = "1.13.1"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.3"
activityCompose = "1.9.0"
composeBom = "2024.04.01"
//grade/libs.versions.toml
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
下载依赖项添加完成后,保证该项目设置了使用Jetpack Compose,通过配置 MainActivity以Compose语糖风格绑定启动UI:
@Composable
fun MainScreen() {
Scaffold {
LoginScreen()
}
}
@Composable
fun LoginScreen() {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var error by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Nim", style = MaterialTheme.typography.displayMedium)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = username,
onValueChange = { username = it },
label = { Text("Username") },
singleLine = true
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
singleLine = true,
visualTransformation = PasswordVisualTransformation()
)
if (error.isNotEmpty()) {
Text(text = error, color = Color.Red, style = MaterialTheme.typography.bodyMedium)
}
Spacer(modifier = Modifier.height(16.dp))
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
Button(onClick = { }) {
Text("Cancel")
}
Button(onClick = {
if (password.length < 8) {
error = "密码必须至少为8个字符。"
} else {
error = ""
}
}) {
Text("Next")
}
}
}
}
它可以让你在Android Studio中实时看到你编写的 UI 组件,不必要每次重新运行应用。这对我们调试和调整界面非常有帮助。
@Preview
注解一般用在 @Composable
函数上方,用于标记函数的 UI 布局可以在 Android Studio 的预览窗口中显示。以下是预览函数的基本结构:
@Preview(showBackground = true)
@Composable
fun LoginScreenPreview() {
NimLoginTheme {
LoginScreen()
}
}
@Preview
像是一个小窗口,帮我们展示 @Composable
函数的实际渲染效果。只要在代码上方加上 @Preview
,就能在 Android Studio 的右侧看到你想看的UI效果。
package com.nim.nimlogin
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.nim.nimlogin.ui.theme.NimLoginTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
NimLoginTheme {
LoginScreen()
}
}
}
}
@Composable
fun LoginScreen() {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var error by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Nim", style = MaterialTheme.typography.displayMedium)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = username,
onValueChange = { username = it },
label = { Text("Username") },
singleLine = true
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
singleLine = true,
visualTransformation = PasswordVisualTransformation()
)
if (error.isNotEmpty()) {
Text(text = error, color = Color.Red, style = MaterialTheme.typography.bodyMedium)
}
Spacer(modifier = Modifier.height(16.dp))
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
Button(onClick = {
// 清除输入
username = ""
password = ""
error = ""
}) {
Text("Cancel")
}
Button(onClick = {
if (password.length < 8) {
error = "密码必须至少为8个字符。"
} else {
error = ""
}
}) {
Text("Next")
}
}
}
}
@Preview(showBackground = true)
@Composable
fun LoginScreenPreview() {
NimLoginTheme {
LoginScreen()
}
}
• 难点:和传统 XML 的状态管理不同,Compose 使用 remember
和 mutableStateOf
管理 UI 状态。如何实时更新界面,确保用户输入体验流畅。
• 解决方案:使用 remember
和 mutableStateOf
保持组件状态,确保状态变化时界面自动更新。
• 难点:实现实时输入验证且提供用户友好的错误提示,防止错误状态被延迟或丢失。
• 解决方案:在 onValueChange
中处理输入验证,通过动态更新错误提示提升用户体验。利用 Text
和 Color
的组合,要多思考怎么设计直观的错误提示样式。
• 难点:在 Compose 中,声明式导航和传统的 Fragment
和 Activity
导航有很大区别,特别是在状态的保留和恢复。
• 解决方案:使用 Navigation Compose
进行页面管理,通过 NavHost
实现页面的解耦和状态管理,使得 UI 流程更顺畅、维护很方便。
我加深了对 Jetpack Compose 的理解,还掌握了如何在实际项目中灵活使用状态管理和组件解耦。我值得分享经验:
Scaffold
、TopAppBar
各等,上手体验非常好,让 UI 更美观一致。State
管理相比传统的 LiveData
和 ViewModel
更灵活,且能更好集成在 UI 交互场景。通过本篇文章的实践,我体验到了 Jetpack Compose 的强大好处是Jetpack Compose声明式编程带来的直观、简化的 UI 构建、灵活的状态管理,以及 Material 3 组件的强大。让我对未来的开发充满了很多期待。相信 Jetpack Compose 在未来几年成为 Android UI 开发的主流工具,希望这篇文章能对大家有所帮助!!
可以参考下,请见GitHub 仓库 - GitHub - jienian/CHAPTS,内容包括Kotlin、ComposeM3 等技术实现。
有任何问题欢迎提问,感谢大家阅读 )
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。