如何在Jetpack Compose中构建可滚动和懒散的组件

273 阅读7分钟

在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 - 一个在 可合成函数中渲染图像的组件。Image
  • Modifier - 一个用于操纵组件外观的属性。
  • Box - 一个用于容纳其他组件的可合成的。
  • Text - 这是一个内置的可合成函数,显示作为参数传递的字符串。

创建一个新的项目

启动Android Studio并创建一个新的Empty Compose Project 。让我们把它命名为Scrolls

Create Compose project

垂直滚动

当使用诸如BoxSurface 等容器时,子元素有时会离开视野。在这种情况下,你可以使用Modifier.horizontalScrollModifier.verticalScroll 修改器来启用各自的滚动方向模式。

在本教程中,我们将重点讨论LazyColumnLazyRow ,因为它们更高级,因为它们有一个内置的滚动行为和几个有用的功能。

创建一个样本列表项

接着,让我们创建一个LazyColumn ,它将包含一个带有imageText 的项目。

一个样本项是用来生成一个具有类似外观但可能包含不同数据/内容的列表项的。

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 是一个默认的主题,是根据项目的名称生成的。如果你给你的项目起了一个不同的名字,那么主题的名字将与此不同。

Sample Item

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)
            }
        }
    }
}

运行应用程序

运行该应用程序后,你应该看到一个可滚动的列表,如下图所示。

Scrollable list

与命令式编程不同,声明式编程在步骤数量和所需逻辑方面相当简单。

用粘性标题对项目进行分组

在下面的例子中,我们将创建一个可滚动的列表,用粘性标题进行分组。每个标题将由一个共同组中的元素的第一个字符来识别。这样一来,每个组将有一个独特的标题。

它被称为粘性标题,因为当用户向下滚动时,它被钉在列表的顶部。

下面的代码是前面例子的进阶。

@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- 是指ListListItem 对象中的name 属性的第一个字符。

注意:Sticky header API仍在开发中,它在未来可能会发生变化。出于这个原因,我们使用实验性的API,在函数中注解了@ExperimentalFoundationApi

Sticky headers

滚动状态

在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中创建可滚动列表的基础知识。我们还学习了如何在可滚动列表中创建粘性标题。从本教程中获得的知识在创建更复杂的可滚动列表时将会很有用。