在Jetpack Compose中构建可滚动和懒散的组件
Jetpack Compose是新的Android UI工具包,采用声明式方法。Compose通过提供一个声明式的API,让你无需改变前端视图就能渲染用户界面,从而使设计和管理用户界面变得简单。
许多应用程序需要对多个项目进行可视化处理。Jetpack Compose包括一组组件,只对组件视口中可见的元素进行编排。
LazyColumn 和 是这些组件中的两个。它们管理项目的显示方式,而不管其数量如何。这使得开发更容易,并为用户提供互动体验。LazyRow
在本教程中,我们将学习构建此类组件的最佳实践。
前提条件
要完成本教程,你需要具备以下条件。
- [Android Studio Arctic Fox (2020.3.1) Stable]或更高版本 - 这些版本的Android Studio有Jetpack Compose插件。
- [关于Jetpack Compose的基本知识]。如果你不熟悉Jetpack Compose,可以随时查看[这个教程]来开始学习。
- 对[Kotlin Coroutines]的良好理解将有助于你理解[Kotlin Coroutines与Jetpack Compose]。
注意:在编写本教程时,Jetpack Compose的版本是1.0.0--它的第一个稳定版本。
术语
在本教程中,我们将使用以下Compose组件。
LazyColumn- 一个管理项目如何垂直显示的组件,无论其数量如何。LazyRow- 管理项目列表如何水平显示的组件。Painter- 一个在 可合成函数中渲染图像的组件。ImageModifier- 一个用于操纵组件外观的属性。Box- 一个用于容纳其他组件的可合成的。Text- 这是一个内置的可合成函数,显示作为参数传递的字符串。
创建一个新的项目
启动Android Studio并创建一个新的Empty Compose Project 。让我们把它命名为Scrolls 。

垂直滚动
当使用诸如Box 或Surface 等容器时,子元素有时会离开视野。在这种情况下,你可以使用Modifier.horizontalScroll 或Modifier.verticalScroll 修改器来启用各自的滚动方向模式。
在本教程中,我们将重点讨论LazyColumn 和LazyRow ,因为它们更高级,因为它们有一个内置的滚动行为和几个有用的功能。
创建一个样本列表项
接着,让我们创建一个LazyColumn ,它将包含一个带有image 和Text 的项目。
一个样本项是用来生成一个具有类似外观但可能包含不同数据/内容的列表项的。
i) 列表项的模型类
一个模型类用来定义数据类型和它在样本项中使用的属性。
data class ListItem(val name: String)
在这里,我们创建了一个ListItem 类,它有一个name 属性。这个类的对象将被用来生成多个项目。
ii) 样本项的可组合视图
在这里,我们需要使用可组合函数来创建实际的项目。我们将使用一个盒子作为Image 的父容器和一个Text
@Composable
fun ListItem(item: ListItem) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
.height(60.dp)
.background(color = Color.Gray)
) {
Row(
modifier = Modifier
.padding(horizontal = 8.dp)
.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "user icon",
modifier = Modifier
.padding(horizontal = 8.dp)
.align(CenterVertically)
)
Text(
modifier = Modifier
.padding(horizontal = 16.dp)
.align(CenterVertically),
text = item.name,
color = Color.White,
fontSize = 16.sp
)
}
}
ListItem 可组合函数将一个ListItem 作为参数,并返回一个Box ,其中包含一个Row ,一个Image 和一个Text 。
在Image 函数中使用的画师资源是在创建一个新项目时自动生成的。你也可以使用你的图像资源。
预览。
@Preview(showBackground = true)
@Composable
fun Preview() {
ScrollsTheme {
ListItem(item = ListItem("John Doe"))
}
}
ScrollsTheme 是一个默认的主题,是根据项目的名称生成的。如果你给你的项目起了一个不同的名字,那么主题的名字将与此不同。

iii) 列表项对象
下面是一个ListItem 对象的数组。这些项目将被用来生成一个可滚动的列表。
private val listItems: List<ListItem> = listOf(
ListItem("Jayme"),
ListItem("Gil"),
ListItem("Juice WRLD"),
ListItem("Callan"),
ListItem("Braxton"),
ListItem("Kyla"),
ListItem("Lil Mosey"),
ListItem("Allan"),
ListItem("Mike"),
ListItem("Drew"),
ListItem("Nia"),
ListItem("Coi Relay")
)
iv) 显示一个可滚动的项目列表
为了显示一个可垂直滚动的列表,我们将创建一个可组合的函数,接受一个列表项作为参数;在这种情况下,就是我们刚刚创建的listItems 。
然后该函数使用items() 方法,该方法是LazyColumn 的一部分,在用户滚动时传递项目。
@Composable
fun DisplayList(items: List<ListItem>) {
LazyColumn(modifier = Modifier.fillMaxSize(1F)) {
items(items) { item ->
ListItem(list = item)
}
}
}
v) 调用DisplayList函数
到此为止,我们只能在预览屏幕上查看一个样本项目。我们需要在onCreate() 方法中调用DisplayList 函数,以便在应用程序运行时显示项目的列表。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ScrollsTheme {
Surface(color = MaterialTheme.colors.background) {
DisplayList(items = listItems)
}
}
}
}
运行应用程序
运行该应用程序后,你应该看到一个可滚动的列表,如下图所示。

与命令式编程不同,声明式编程在步骤数量和所需逻辑方面相当简单。
用粘性标题对项目进行分组
在下面的例子中,我们将创建一个可滚动的列表,用粘性标题进行分组。每个标题将由一个共同组中的元素的第一个字符来识别。这样一来,每个组将有一个独特的标题。
它被称为粘性标题,因为当用户向下滚动时,它被钉在列表的顶部。
下面的代码是前面例子的进阶。
@ExperimentalFoundationApi
@Composable
fun DisplayList(items: List<ListItem>) {
LazyColumn(modifier = Modifier.fillMaxSize(1F)) {
// Note: Grouping should be done in a viewModel
val grouped = items.groupBy { it.name[0] }
grouped.forEach {initial, items ->
stickyHeader {
Text(
text = initial.toString(),
modifier = Modifier.padding(10.dp)
)
}
items(items) { item ->
ListItem(item = item)
}
}
}
}
解释一下。
- grouped- 是一个
Map,包含一个List,由name属性的第一个字符分组的ListItem对象。 - initial- 是指
List的ListItem对象中的name属性的第一个字符。
注意:Sticky header API仍在开发中,它在未来可能会发生变化。出于这个原因,我们使用实验性的API,在函数中注解了
@ExperimentalFoundationApi

滚动状态
在Jetpack Compose中,状态是一个对象的属性,可以在重组过程中改变。我们使用一个内置的构造函数LazyStates 来创建一个状态对象,用来追踪可滚动组件的状态和交互。
例子。
@Composable
fun DisplayList(items: List<ListItem>) {
val listState = rememberLazyState()
// Update the state
LazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(1F)) {
...
}
滚动位置
一个LazyState也可以用来跟踪列表中的项目的位置。使用上面例子中的listState,我们可以跟踪当前的滚动位置或滚动到某个有效位置。
这是通过使用该状态的scrollToItem() 方法来实现的。这个方法启动了一个可暂停的工作,因此需要使用Coroutines。
添加Coroutines依赖性
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0")
实施示例
fun DisplayList(items: List<ListItem>) {
val listState = rememberLazyListState()
LazyColumn(modifier = Modifier.fillMaxSize(1F), state = listState) {
val grouped = items.groupBy { it.name[0] }
grouped.forEach { initial, items ->
stickyHeader {
Text(
text = initial.toString(),
modifier = Modifier.padding(10.dp)
)
}
items(items) { item ->
ListItem(item = item)
}
}
CoroutineScope(Dispatchers.MAIN).launch {
listState.scrollToItem(items.size-1)
}
}
}
为了演示,我们已经滚动到了列表中的最后一个项目。这可以被推进,使列表滚动到某个记忆中的位置,特别是在配置改变或重新组合的时候。
显示一个水平滚动的列表
顾名思义,垂直和水平可滚动组件的主要区别在于方向。LazyRow 是一个可以用来显示水平列表的可滚动组件。在实现方面,它可以与LazyColumn相媲美。
例子。
@Composable
fun LazyRowExample(items: List<ListItem>) {
// implement LazyRow
LazyRow(
contentPadding = PaddingValues(4.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.fillMaxSize(1F)
) {
// display items horizontally
items(items) { item ->
ListItem(list = item)
}
}
}
结语
在本教程中,我们已经介绍了在Jetpack Compose中创建可滚动列表的基础知识。我们还学习了如何在可滚动列表中创建粘性标题。从本教程中获得的知识在创建更复杂的可滚动列表时将会很有用。