Jetpack Compose中的列表(List)组件(LazyColumn/LazyRow/LazyVerticalGrid/LazyHorizontalGrid)

前言

Jetpack Compose 中的列表组件相对于之前的View方式要简单很多。

列表

竖向列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Composable
fun MyList(mList: List<String>){
LazyColumn {
items(mList.size){
ListItem(mList[it])
}
}
}

@Composable
fun ListItem(text: String) {
// 构建列表项的 UI
Text(text = text)
}

调用

1
MyList(listOf("张三","李四","王五"))

注意

新版本的items方法参数变了。

遍历带索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Composable
fun ZhangeJieList(mList: List<String>, itemClick: (Int) -> Unit) {
LazyColumn {
itemsIndexed(mList) { index, item ->
ListItem(index, item)
}

}

}

@Composable
fun ListItem(index: Int, text: String) {
// 构建列表项的 UI
Text(text = text)
}

横向列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Composable
fun MyList(mList: List<String>){
LazyRow {
items(mList.size){
ListItem(mList[it])
}
}
}

@Composable
fun ListItem(text: String) {
// 构建列表项的 UI
Text(text = text)
}

调用

1
MyList(mutableListOf<String>("A", "B", "C"))

设置间距

1
horizontalArrangement= Arrangement.spacedBy(10.dp)

项间距

LazyColumn

1
LazyColumn(verticalArrangement = Arrangement.spacedBy(10.dp))

LazyRow

1
LazyRow(horizontalArrangement = Arrangement.spacedBy(10.dp))

详细示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Composable
fun MyList(mList: List<String>, modifier: Modifier) {
LazyRow(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
itemsIndexed(mList) { index, item ->
ListItem(index, item)
}

}
}

@Composable
fun ListItem(index: Number, text: String) {
Button(
modifier = Modifier
.width(100.dp)
.height(40.dp)
.background(Color.Transparent),
shape = CommonTheme.CornerM,
onClick = {
Log.i("TAG", "ListItem: $index")
}) {
Text(
text = text,
fontSize = 16.sp,
color = Color.White
)
}
}

点击事件带索引

水平

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.xhkjedu.zxs_android.R
import com.xhkjedu.zxs_android.componts.ImgLocalBg

data class TopMenu(var name: String, var selected: Boolean = false)

@Composable
fun MainTopMenuList(
mList: MutableList<TopMenu>,
modifier: Modifier = Modifier.fillMaxSize(),
itemClick: (item: TopMenu, index: Int) -> Unit
) {
LazyRow(modifier = modifier) {
itemsIndexed(mList) { index, item ->
MainTopMenuItem(index, item, itemClick)
}

}
}


@Composable
fun MainTopMenuItem(index: Int, item: TopMenu, itemClick: (item: TopMenu, index: Int) -> Unit) {
Column(
modifier = Modifier
.fillMaxHeight()
.padding(start = 16.dp, end = 16.dp)
.clickable {
itemClick(item, index)
}
) {
Box(
modifier = Modifier
.weight(1f)
.align(Alignment.CenterHorizontally)
) {
Text(
item.name,
color = Color.White,
fontSize = if (item.selected) 23.sp else 19.sp,
modifier = Modifier.align(Alignment.Center)
)
}
Box(
modifier = Modifier
.size(16.dp)
.align(Alignment.CenterHorizontally)
) {
if (item.selected) {
ImgLocalBg(R.drawable.main_menu_bottom)
}
}
}
}

垂直

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 任务列表
@Composable
fun MyTaskList(mList: List<String>, modifier: Modifier) {
LazyColumn(modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
itemsIndexed(mList) { index, item ->
MyTaskListItem(index, item)
}

}
}

@Composable
fun MyTaskListItem(index: Number, text: String) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.background(
color = Color(0x660600BF),
shape = CommonTheme.CornerXs
)
.clip(CommonTheme.CornerXs)
.clickable {
Log.i("TAG", "ListItem: $index")
}
) {
Text(
text = text,
fontSize = 16.sp,
color = Color.White
)
}
}

高度等分

有这种需求

我们的列表有固定的项数,我们想让项的高度相等,均分父列表,并且能自适应父列表。

但是LazyColumn没有直接均分的属性,weight也是不能用的,该怎样实现呢?

获取列表的高度减去所有的间距,在计算项的高度,传给Item。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Composable
fun MyTaskList(mList: List<String>, modifier: Modifier) {

// 存储可用高度
val containerHeight = remember { mutableStateOf(0.dp) }
val density = LocalDensity.current
Box(modifier = modifier) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.onSizeChanged { size ->
containerHeight.value = with(density) { size.height.toDp() }
},
// 设置项之间的间距(水平和垂直方向)
verticalArrangement = Arrangement.spacedBy(12.dp), // 垂直间距
) {
val itemHeight = (containerHeight.value - 12.dp * (mList.size - 1)) / mList.size
itemsIndexed(mList) { index, item ->
MyTaskListItem(index, item, itemHeight)
}
}
}

}

@Composable
fun MyTaskListItem(index: Number, text: String, itemHeight: Dp) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(itemHeight)
.background(
color = Color(0x660600BF),
shape = CommonTheme.CornerXs
)
.clip(CommonTheme.CornerXs)
.clickable {
Log.i("TAG", "ListItem: $index")
},
verticalAlignment = Alignment.CenterVertically
) {

}
}

Grid

竖向Grid

可以理解为竖向列表,只不过一行可以设置多列。

基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Composable
fun MyGridList(mList: List<String>) {
LazyVerticalGrid(columns = GridCells.Fixed(2)) {
items(mList.size) { index ->
// 在这里构建您的列表项
GridItem(text = mList[index])
}
}
}

@Composable
fun GridItem(text: String) {
// 构建列表项的 UI
Card(
modifier = Modifier.padding(16.dp).height(30.dp),
) {
Text(text)
}
}

注意

竖向Grid布局中的子项,也就是上面的GridItem中的根组件的宽度是自动使用父的,设置宽度不会生效。

横竖屏不同列数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import android.content.res.Configuration
import androidx.compose.ui.platform.LocalConfiguration

@Composable
fun MyGridList(mList: List<String>) {
val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
val columnCount = if (isLandscape) 2 else 1
LazyVerticalGrid(columns = GridCells.Fixed(columnCount)) {
items(mList.size) { index ->
// 在这里构建您的列表项
GridItem(text = mList[index])
}
}
}

@Composable
fun GridItem(text: String) {
// 构建列表项的 UI
Card(
modifier = Modifier.padding(16.dp).height(30.dp),
) {
Text(text)
}
}

自适应列数

这种方式好在,它能自适应进行布局。

假如页面的宽度是700dp,我们设置minSize = 300.dp,这样它会自动变成两列,每列350dp

如果页面的宽度变成了500dp,那么就会变成一列,列的宽度也是500dp

总之还是比较智能的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Composable
fun AppListView(mList: List<AppModel>) {
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 300.dp),
contentPadding = PaddingValues(10.dp),
verticalArrangement = Arrangement.Top
) {
items(mList.size,key = {item -> item }) {
Box(
modifier = Modifier
.padding(10.dp)
.fillMaxSize()
) {
AppListItem(mList[it])
}
}
}
}

设置间距

1
2
3
4
5
6
7
8
9
LazyVerticalGrid(
columns = GridCells.Fixed(2),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
items(photos) { item ->
PhotoItem(item)
}
}

横向Grid

可以理解为横向列表,只不过一列可以设置多行。

基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Composable
fun MyGridList(mList: List<String>) {
LazyHorizontalGrid(rows = GridCells.Fixed(2)) {
items(mList.size) { index ->
// 在这里构建您的列表项
GridItem(text = mList[index])
}
}
}

@Composable
fun GridItem(text: String) {
// 构建列表项的 UI
Card(
modifier = Modifier.padding(16.dp).height(30.dp),
) {
Text(text)
}
}

注意

竖向Grid布局中的子项,也就是上面的GridItem中的根组件的高度是自动使用父的,设置高度不会生效。

Grid间距

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Composable
fun MyTaskList(mList: List<String>, modifier: Modifier) {
LazyVerticalGrid(
modifier = modifier,
columns = GridCells.Fixed(1),
// 设置项之间的间距(水平和垂直方向)
horizontalArrangement = Arrangement.spacedBy(12.dp), // 水平间距
verticalArrangement = Arrangement.spacedBy(12.dp), // 垂直间距
) {
itemsIndexed(mList) { index, item ->
MyTaskListItem(index, item)
}
}
}