Skip to content

Compose 列表组件

欢迎来到 Compose 列表组件的世界!在移动应用开发中,列表是最常用的 UI 模式之一,用于显示大量数据。Compose 提供了强大的列表组件,让我们一起探索吧!

🎯 什么是列表?

列表是一种用于显示多个相似项目的 UI 组件。在 Compose 中,列表通常用于:

  • 显示联系人列表
  • 展示产品列表
  • 列出新闻文章
  • 等等...

Compose 提供了两种主要的列表组件:

  • LazyColumn:用于垂直滚动列表
  • LazyRow:用于水平滚动列表

🧩 LazyColumn 组件

LazyColumn 用于创建垂直滚动的列表,它只会组合和渲染可见的项目,这使得它非常高效,即使列表包含数千个项目。

基本用法

kotlin
@Composable
fun MySimpleList() {
    LazyColumn {
        items(100) { index ->
            Text(
                text = "Item $index",
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}

显示对象列表

你可以使用 items 函数的另一个重载版本来显示对象列表:

kotlin
// 定义数据类
data class User(val id: Int, val name: String, val email: String)

@Composable
fun MyUserList() {
    val users = listOf(
        User(1, "John Doe", "john@example.com"),
        User(2, "Jane Smith", "jane@example.com"),
        User(3, "Bob Johnson", "bob@example.com")
    )
    
    LazyColumn {
        items(users) { user ->
            UserItem(user)
        }
    }
}

@Composable
fun UserItem(user: User) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        Box(
            modifier = Modifier
                .size(40.dp)
                .background(Color.Blue)
                .clip(CircleShape)
        ) {
            Text(
                text = user.name.first().toString(),
                color = Color.White,
                modifier = Modifier.align(Alignment.Center)
            )
        }
        Column(modifier = Modifier.padding(start = 16.dp)) {
            Text(text = user.name, style = TextStyle(fontWeight = FontWeight.Bold))
            Text(text = user.email, style = TextStyle(fontSize = 14.sp, color = Color.Gray))
        }
    }
}

添加分隔线

你可以使用 item 函数在列表中添加分隔线:

kotlin
@Composable
fun MyListWithDividers() {
    LazyColumn {
        itemsIndexed(users) { index, user ->
            UserItem(user)
            if (index < users.size - 1) {
                Divider(modifier = Modifier.padding(horizontal = 16.dp))
            }
        }
    }
}

添加头部和底部

你可以使用 item 函数在列表中添加头部和底部:

kotlin
@Composable
fun MyListWithHeaderFooter() {
    LazyColumn {
        // 头部
        item {
            Text(
                text = "User List",
                style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold),
                modifier = Modifier.padding(16.dp)
            )
        }
        
        // 列表项
        items(users) {
            UserItem(it)
            Divider(modifier = Modifier.padding(horizontal = 16.dp))
        }
        
        // 底部
        item {
            Text(
                text = "Total Users: ${users.size}",
                style = TextStyle(fontSize = 14.sp, color = Color.Gray),
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}

🧩 LazyRow 组件

LazyRow 用于创建水平滚动的列表,用法与 LazyColumn 类似:

基本用法

kotlin
@Composable
fun MySimpleRow() {
    LazyRow {
        items(50) { index ->
            Box(
                modifier = Modifier
                    .size(100.dp)
                    .background(Color.Blue)
                    .padding(8.dp)
            ) {
                Text(text = "Item $index", color = Color.White)
            }
        }
    }
}

添加头部和底部

kotlin
@Composable
fun MyRowWithHeaderFooter() {
    LazyRow(modifier = Modifier.padding(16.dp)) {
        // 头部
        item {
            Text(text = "Start", modifier = Modifier.padding(8.dp))
        }
        
        // 列表项
        items(20) { index ->
            Box(
                modifier = Modifier
                    .size(80.dp)
                    .background(Color.Blue)
                    .padding(8.dp)
            ) {
                Text(text = "$index", color = Color.White)
            }
        }
        
        // 底部
        item {
            Text(text = "End", modifier = Modifier.padding(8.dp))
        }
    }
}

🧩 网格布局

LazyVerticalGrid

LazyVerticalGrid 用于创建垂直滚动的网格布局:

kotlin
@Composable
fun MyVerticalGrid() {
    LazyVerticalGrid(
        columns = GridCells.Fixed(2), // 固定 2 列
        modifier = Modifier.padding(16.dp),
        content = {
            items(20) { index ->
                Box(
                    modifier = Modifier
                        .size(150.dp)
                        .background(Color.Blue)
                        .padding(8.dp)
                ) {
                    Text(text = "Item $index", color = Color.White)
                }
            }
        }
    )
}

LazyHorizontalGrid

LazyHorizontalGrid 用于创建水平滚动的网格布局:

kotlin
@Composable
fun MyHorizontalGrid() {
    LazyHorizontalGrid(
        rows = GridCells.Fixed(3), // 固定 3 行
        modifier = Modifier.height(300.dp).padding(16.dp),
        content = {
            items(30) { index ->
                Box(
                    modifier = Modifier
                        .size(100.dp)
                        .background(Color.Blue)
                        .padding(8.dp)
                ) {
                    Text(text = "Item $index", color = Color.White)
                }
            }
        }
    )
}

🎯 列表性能优化

1. 使用 key 参数

当列表数据发生变化时,使用 key 参数可以帮助 Compose 更高效地更新列表:

kotlin
@Composable
fun MyListWithKeys() {
    LazyColumn {
        items(users, key = { it.id }) { user ->
            UserItem(user)
        }
    }
}

2. 避免在 items 中创建 Composable 函数

尽量避免在 items 函数中创建新的 Composable 函数,这会影响性能:

kotlin
// 不好的做法
@Composable
fun MyBadList() {
    LazyColumn {
        items(users) {
            @Composable
            fun UserItem() {
                // 用户项内容
            }
            UserItem()
        }
    }
}

// 好的做法
@Composable
fun MyGoodList() {
    LazyColumn {
        items(users) {
            UserItem(it)
        }
    }
}

@Composable
fun UserItem(user: User) {
    // 用户项内容
}

3. 避免不必要的重新组合

使用 rememberderivedStateOf 来避免不必要的重新组合:

kotlin
@Composable
fun MyOptimizedList() {
    val users by remember { mutableStateOf(getUsers()) }
    val searchQuery by remember { mutableStateOf("") }
    
    // 使用 derivedStateOf 只在依赖项变化时重新计算
    val filteredUsers by remember {
        derivedStateOf {
            if (searchQuery.isEmpty()) {
                users
            } else {
                users.filter { it.name.contains(searchQuery, ignoreCase = true) }
            }
        }
    }
    
    LazyColumn {
        items(filteredUsers) {
            UserItem(it)
        }
    }
}

🎯 列表的高级用法

列表项动画

你可以为列表项添加动画效果:

kotlin
@Composable
fun MyAnimatedList() {
    LazyColumn {
        items(users) { user ->
            val animatedVisibility = rememberAnimatedVisibility(visible = true)
            animatedVisibility(enter = fadeIn() + slideInVertically(), exit = fadeOut() + slideOutVertically()) {
                UserItem(user)
            }
        }
    }
}

列表项交互

你可以为列表项添加点击、长按等交互效果:

kotlin
@Composable
fun MyInteractiveList() {
    val selectedUser by remember { mutableStateOf<User?>(null) }
    
    LazyColumn {
        items(users) { user ->
            val isSelected = selectedUser == user
            UserItem(
                user = user,
                isSelected = isSelected,
                onClick = { selectedUser = user },
                onLongClick = { /* 处理长按事件 */ }
            )
        }
    }
}

@Composable
fun UserItem(user: User, isSelected: Boolean, onClick: () -> Unit, onLongClick: () -> Unit) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
            .background(if (isSelected) Color.LightGray else Color.Transparent)
            .clickable(onClick = onClick)
            .pointerInput(Unit) {
                detectTapGestures(onLongPress = { onLongClick() })
            }
    ) {
        // 用户项内容
    }
}

🎨 练习

现在,让我们来练习一下:创建一个包含懒加载列表的应用,显示联系人列表,并支持搜索功能。

kotlin
@Composable
fun MyContactListApp() {
    var searchQuery by remember { mutableStateOf("") }
    val contacts = remember { getContacts() }
    
    // 过滤联系人
    val filteredContacts by remember {
        derivedStateOf {
            if (searchQuery.isEmpty()) {
                contacts
            } else {
                contacts.filter { it.name.contains(searchQuery, ignoreCase = true) }
            }
        }
    }
    
    Column(modifier = Modifier.fillMaxSize()) {
        // 搜索栏
        TextField(
            value = searchQuery,
            onValueChange = { searchQuery = it },
            label = { Text(text = "Search contacts") },
            leadingIcon = { Icon(imageVector = Icons.Filled.Search, contentDescription = null) },
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp)
        )
        
        // 联系人列表
        if (filteredContacts.isEmpty()) {
            // 空列表提示
            Text(
                text = "No contacts found",
                modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.Center)
            )
        } else {
            // 联系人列表
            LazyColumn {
                items(filteredContacts) { contact ->
                    ContactItem(contact)
                    Divider(modifier = Modifier.padding(horizontal = 16.dp))
                }
            }
        }
    }
}

@Composable
fun ContactItem(contact: Contact) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
            .clickable { /* 处理点击事件 */ }
    ) {
        Box(
            modifier = Modifier
                .size(48.dp)
                .background(Color.Blue)
                .clip(CircleShape)
        ) {
            Text(
                text = contact.name.first().toString(),
                color = Color.White,
                style = TextStyle(fontWeight = FontWeight.Bold),
                modifier = Modifier.align(Alignment.Center)
            )
        }
        Column(modifier = Modifier.padding(start = 16.dp)) {
            Text(text = contact.name, style = TextStyle(fontWeight = FontWeight.Bold))
            Text(text = contact.phoneNumber, style = TextStyle(fontSize = 14.sp, color = Color.Gray))
        }
        Icon(imageVector = Icons.Filled.ArrowForward, contentDescription = null, modifier = Modifier.padding(start = 8.dp))
    }
}

🎉 恭喜

你已经学习了 Compose 中的列表组件!从基本的 LazyColumn 和 LazyRow 到网格布局和性能优化,这些知识将帮助你创建高效、美观的列表 UI。

下一节,我们将学习 Compose 中的动画,了解如何为应用添加生动的动画效果。

🚀 继续前进吧!