Android Jetpack Compose基础实践
文章目录
- 创建一个新的工程
- Compose 入门
- 微调界面
- 修饰符
- 重复使用可组合项
- 创建Compose中的列(Column)和行(Row)
- Compose 和 Kotlin
- 添加按钮
- Compose中的状态(State)
- 参考文献
在本教程,将实践 Jetpack Compose 的基本用法。
创建一个新的工程
选择Phone and Tablet选项卡中Empty Activity,
将工程命名为BasicsCodelab。
Compose 入门
Android 使用@Composable注解来表示可组合函数,从而定义UI。使用Compose时,Activity 仍然是 Android 应用的入口点。在Compose中,setContent 来定义布局,但不同于在传统 View 系统中使用 XML 文件,将在该函数中调用可组合函数。此外,若要使用 Android Studio 预览,只需使用 @Preview 注解标记所有无参数可组合函数或采用默认形参的函数,然后构建项目即可。项目的代码如下图所示:
微调界面
为Greeting 设置不同的背景色,使用Surface 包围 Text 可组合项。Surface 会采用一种颜色,因此请使用 MaterialTheme.colorScheme.primary。
Surface 和 MaterialTheme 是与 Material Design 相关的概念。Material Design 是 Google 提供的一个设计体系,旨在帮助构建界面和体验。
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {Surface(color = MaterialTheme.colorScheme.primary) {Text(text = "Hello $name!",modifier = modifier)}
}
上述代码将嵌套在 Surface 内的组件将在该背景颜色之上绘制。
预览的效果:
文字颜色更改成白色是Material 组件提供的功能,为了获得更好的UI体验。在这种情况下,Surface 会了解,当该背景设置为 primary 颜色后,其上的任何文本都应使用 onPrimary 颜色,此颜色也在主题中进行了定义。
修饰符
大多数 Compose 界面元素(例如 Surface 和 Text)都接受可选的 modifier 参数。修饰符会指示界面元素如何在其父布局中放置、显示或表现。Greeting 可组合项已有一个默认修饰符,该修饰符随后会传递给 Text。
可以试着修改 modifier 的属性,例如,padding 修饰符会在其修饰的元素周围应用一定的空间。
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.unit.dp
// ...@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {Surface(color = MaterialTheme.colorScheme.primary) {Text(text = "Hello $name!",modifier = modifier.padding(24.dp))}
}
为默认修饰符添加内边距修饰符:modifier.padding(24.dp)。形成如下效果:
重复使用可组合项
添加到界面的组件越多,创建的嵌套层级就越多。如果函数变得非常大,可能会影响可读性。通过创建可重用的小型组件,可以轻松构建应用中所用界面元素的库。每个组件对应于屏幕的一个部分,可以单独修改。
最佳实践是,函数应包含一个修饰符参数,系统默认为该参数分配空修饰符。将此修饰符转发到您在函数内调用的第一个可组合项。这样,调用点就可以在可组合函数之外调整布局指令和行为了。
创建一个MyApp 的可组合项,该组合项中包含Greeting。
@Composable
fun MyApp(modifier: Modifier = Modifier) {Surface(modifier = modifier,color = MaterialTheme.colorScheme.background) {Greeting("Android")}
}
现在可以重复使用 MyApp 可组合项,可以省去 onCreate 回调和预览,从而避免重复编写代码。
在预览中,调用 MyApp 并移除预览的名称。MainActivity.kt 的代码应如下:
package com.example.basicscodelabimport android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.basicscodelab.ui.theme.BasicsCodelabThemeclass MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {BasicsCodelabTheme {MyApp(modifier = Modifier.fillMaxSize())}}}
}@Composable
fun MyApp(modifier: Modifier = Modifier) {Surface(modifier = modifier,color = MaterialTheme.colorScheme.background) {Greeting("Android")}
}@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {Surface(color = MaterialTheme.colorScheme.primary) {Text(text = "Hello $name!",modifier = modifier.padding(24.dp))}
}@Preview(showBackground = true)
@Composable
fun GreetingPreview() {BasicsCodelabTheme {MyApp()}
}
创建Compose中的列(Column)和行(Row)
Compose 中的三个基本标准布局元素是 Column、Row 和 Box 可组合项。
它们是接受可组合内容的可组合函数,因此您可以在其中放置项目。例如,Column 中的每个子级都将垂直放置。现在尝试更改Greeting,实现如下效果:
代码方案为:
import androidx.compose.foundation.layout.Column
// ...@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {Surface(color = MaterialTheme.colorScheme.primary) {Column(modifier = modifier.padding(24.dp)) {Text(text = "Hello ")Text(text = name)}}
}
Compose 和 Kotlin
可组合函数可以像 Kotlin 中的其他函数一样使用。这会使界面构建变得非常有效,可以添加语句来影响界面的显示方式。
例如,修改之前定义的MyApp
@Composable
fun MyApp(modifier: Modifier = Modifier,names: List<String> = listOf("World", "Compose")
) {Column(modifier) {for (name in names) {Greeting(name = name)}}
}
使用循环向Column中添加元素。效果如下:
当前尚未设置可组合项的尺寸,也未对可组合项的大小添加任何限制,因此每一行仅占用可能的最小空间,预览时的效果也是如此。更改预览效果,以模拟小屏幕手机的常见宽度 320dp。按如下所示向 @Preview 注解添加 widthDp 参数:
@Preview(showBackground = true, widthDp = 320)
@Composable
fun GreetingPreview() {BasicsCodelabTheme {MyApp()}
}
接下来为修饰符(modifier)添加更多的属性,来查看显示效果。使用 fillMaxWidth 和 padding 修饰符复制以下布局。注意更改工程中的代码,使用以下代码修改Compose函数。
import androidx.compose.foundation.layout.fillMaxWidth@Composable
fun MyApp(modifier: Modifier = Modifier,names: List<String> = listOf("World", "Compose")
) {Column(modifier = modifier.padding(vertical = 4.dp)) {for (name in names) {Greeting(name = name)}}
}@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {Surface(color = MaterialTheme.colorScheme.primary,modifier = modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {Column(modifier = Modifier.fillMaxWidth().padding(24.dp)) {Text(text = "Hello ")Text(text = name)}}
}
得到如下的效果:
添加按钮
向 Greeting 添加可点击元素,因此需要先添加对应的按钮。Button 是 material3 软件包提供的一种可组合项,它采用可组合项作为最后一个参数,同时提供了多种类型,如ElevatedButton、FilledTonalButton、OutlinedButton 和 TextButton等。
由于没有 alignEnd 修饰符,因此您需要在开始时为该可组合项赋予一定的 weight。weight 修饰符会让元素填满所有可用空间,使其“具有弹性”,也就是会推开其他没有权重的元素(即“无弹性”元素)。该修饰符还会使 fillMaxWidth 修饰符变得多余。
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.ElevatedButton
// ...@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {Surface(color = MaterialTheme.colorScheme.primary,modifier = modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {Row(modifier = Modifier.padding(24.dp)) {Column(modifier = Modifier.weight(1f)) {Text(text = "Hello ")Text(text = name)}ElevatedButton(onClick = { /* TODO */ }) {Text("Show more")}}}
}
Compose中的状态(State)
在本部分中,将向屏幕中添加一些互动。到目前为止,已经创建了一些静态布局,但现在要让它们响应用户更改。
实现上述效果的具体代码:
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {val expanded = remember { mutableStateOf(false) }val extraPadding = if (expanded.value) 48.dp else 0.dpSurface(color = MaterialTheme.colorScheme.primary,modifier = modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {Row(modifier = Modifier.padding(24.dp)) {Column(modifier = Modifier.weight(1f).padding(bottom = extraPadding)) {Text(text = "Hello ")Text(text = name)}ElevatedButton(onClick = { expanded.value = !expanded.value }) {Text(if (expanded.value) "Show less" else "Show more")}}}
}
在模拟器或者真机中运行,会看到每项内容可以单独展开。
在该例中,需要在某个位置存储某个值,用于指示每项内容是否展开(即内容的状态),这里使用 expanded 来表示布尔值。单纯更改 expanded 变量不会触发界面重组的原因是 Compose 并未跟踪此更改。此外,每次调用 Greeting 时,都会将该变量重置为 false。如需向可组合项添加内部状态,可以使用 mutableStateOf 函数,该函数可让 Compose 重组读取该 State 的函数。具体而言,mutableStateOf 是 Jetpack Compose 中用于 状态管理 的关键函数之一,它可以创建一个 可观察的、可变的状态对象,从而实现 UI 和状态的自动绑定与更新。
不能只是将 mutableStateOf 分配给可组合项中的某个变量。如前所述,重组可能会随时发生,这会再次调用可组合项,从而将状态重置为值为 false 的新可变状态。如需在重组后保留状态,请使用 remember 记住可变状态。remember 可以起到保护作用,防止状态在重组时被重置。
这里通过为“onClick”指定 lambda 表达式,定义点击时将执行的操作。例如,切换展开状态的值,并根据该值显示不同的文本。extraPadding 则提供了展开之后的额外间距。
参考文献
- Jetpack Compose 教程
- Jetpack Compose 基础知识
- Kotlin docs