现在应用市场上很多产品都少不了列表展示需求场景,例如通讯录,短信,音乐列表等等。
所以本篇文章讲解的组件 - 列表 (LazyList 和 LazyRow )
在了解 LazyList 和 LazyRow 之前,我们先了解下 LazyListScope作用域
LazyColumn 和 LazyRow 内部都是继承 LazyList组件 实现,但 LazyList 不能直接使用
LazyList 的 content 是一个 LazyListScope.() -> Unit 类型的作用域
LazyListScope 提供了 item , items(Int) , item(List) , itemsIndexed(List) 扩展函数来展示列表内容
item:展示单项数据
items(Int):展示多项整型数据
items(List) 展示一组集合数据
itemsIndexed(List) 展示一组集合数据,并且带有下标
val list = ('A'..'Z').map { it.toString() }
LazyColumn{
item { Text(text = "first item") }
items(10){ index ->
Text(text = "$index")
}
item { Text(text = "last item") }
items(list){ item ->
Text(text = item)
}
itemsIndexed(list){ index, item ->
Text(text = "$index/$item")
}
}
LazyColumn 就是一个纵向滚动列表,用来显示一组纵向数据,并且可以滑动列表
@Composable
fun LazyColumn(
modifier: Modifier = Modifier, //修饰符
state: LazyListState = rememberLazyListState(), //记录列表位置状态
contentPadding: PaddingValues = PaddingValues(0.dp), //整体内容周围的一个边距
reverseLayout: Boolean = false, //是否反转列表
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, //子组件纵向对齐方式
horizontalAlignment: Alignment.Horizontal = Alignment.Start, //子组件横向对齐方式
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), //fling行为处理逻辑
userScrollEnabled: Boolean = true, //是否允许滑动
content: LazyListScope.() -> Unit //LazyList作用域
)
LazyColumn 组件 相比传统的 RecyclerView 少写很多代码
RecyclerView 需要在xml中声明一个RecyclerView控件,再在xml中声明一个子控件,再创建一个适配器Adapter,最后在activity中 指定 RecyclerView 的布局类型,再为其填充数据
LazyColumn 就很简单了,看如下代码的实现,就知道了
@Composable
fun LazyColumnList() {
LazyColumn {
items(20) { i ->
Text(
text = "Item $i",
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
)
}
}
}
Padding可以设置列表边距,但是会出现切割现象,我们来看下代码
val list = ('A'..'Z').map { it.toString() }
LazyColumn {
itemsIndexed(list) { index, letter ->
Card(
modifier = Modifier
.width(120.dp)
.height(200.dp)
.padding(10.dp)
) {
Text(
text = "$index $letter",
textAlign = TextAlign.Center,
fontSize = 20.sp,
modifier = Modifier
.fillMaxSize()
.wrapContentHeight(Alignment.CenterVertically)
)
}
}
}
注:
从上图可以看出来最后一个出现了被切割现象,那如何解决这个问题呢?
那就要用到 LazyColumn 提供的 contentPadding 解决
LazyColumn 设置边距用的是contentPadding,能保证上下两边的边距相等同时,还不会在滚动的时候,出现切割现象
val list = ('A'..'Z').map { it.toString() }
LazyColumn(contentPadding = PaddingValues(top = 40.dp, start = 10.dp, bottom = 40.dp, end = 10.dp)) {
itemsIndexed(list) { index, letter ->
Card(
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
// .padding(10.dp)
) {
Text(
text = "$index $letter",
textAlign = TextAlign.Center,
fontSize = 20.sp,
modifier = Modifier.fillMaxSize().wrapContentHeight(Alignment.CenterVertically)
)
}
}
}
注:
现在可以看出来,没有切割的现象了,但有个问题,中间没有了间距
Lazy Layout提供了专门给子项之间设置边距的属性,使用Arrangement.spacedBy()即可
Arrangement.spacedBy() 专门为子项设置边距
val list = ('A'..'Z').map { it.toString() }
LazyColumn(contentPadding = PaddingValues(top = 40.dp, start = 10.dp, bottom = 40.dp, end = 10.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)) {
itemsIndexed(list) { index, letter ->
Card(
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
) {
Text(
text = "$index $letter",
textAlign = TextAlign.Center,
fontSize = 20.sp,
modifier = Modifier.fillMaxSize()
.wrapContentHeight(Alignment.CenterVertically)
)
}
}
}
有时候有这样的需求:让组件随着列表的滚动进行一些额外的响应。如随着滚动隐藏和显示某个组件
这时候 rememberLazyListState 就排上用场了
以下是rememberLazyListState 源码:
@Composable
fun rememberLazyListState(
initialFirstVisibleItemIndex: Int = 0, //第一个可见子项元素的下标
initialFirstVisibleItemScrollOffset: Int = 0 //第一个可见子项元素的偏移距离
): LazyListState {
return rememberSaveable(saver = LazyListState.Saver) {
LazyListState(
initialFirstVisibleItemIndex,
initialFirstVisibleItemScrollOffset
)
}
}
根据列表第一项可见显示组件,不可见隐藏组件
@SuppressLint("FrequentlyChangedStateReadInComposition")
@Composable
fun ListLayout() {
val state = rememberLazyListState()
Box {
val list = ('A'..'Z').map { it.toString() }
LazyColumn(state = state,
contentPadding = PaddingValues(top = 40.dp, start = 10.dp, bottom = 40.dp, end = 10.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)) {
itemsIndexed(list) { index, letter ->
Card(
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
) {
Text(
text = "$index $letter",
textAlign = TextAlign.Center,
fontSize = 20.sp,
modifier = Modifier
.fillMaxSize()
.wrapContentHeight(Alignment.CenterVertically)
)
}
}
}
if (state.firstVisibleItemIndex == 0) {
FloatingActionButton(
onClick = {},
shape = CircleShape,
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(20.dp)
) {
Icon(Icons.Filled.Star, "Add Button")
}
}
}
}
因为compose中往列表添加一项数据,就会整体往后移,数据越多,页面性能越差
所以Google给出了一个解决方案:在items函数添加一个唯一标识的key
@Composable
fun SubVerticalScrollable() {
val list = ('A'..'Z').map { it.toString() }
LazyColumn(modifier = Modifier.height(300.dp)) {
items(list, key = { it }) { letter ->
...
}
}
}
LazyRow 和 LazyColumn用法基本相同,唯一不同的是横向布局的列表组件
@Composable
fun LazyRow(
modifier: Modifier = Modifier, //修饰符
state: LazyListState = rememberLazyListState(), //记录列表位置状态
contentPadding: PaddingValues = PaddingValues(0.dp), //整体内容周围的一个边距
reverseLayout: Boolean = false, //是否反转列表
horizontalArrangement: Arrangement.Horizontal =
if (!reverseLayout) Arrangement.Start else Arrangement.End, //子组件横向对齐方式
verticalAlignment: Alignment.Vertical = Alignment.Top, //子组件纵向对齐方式
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), //fling行为处理逻辑
userScrollEnabled: Boolean = true, //是否允许滑动
content: LazyListScope.() -> Unit //LazyList作用域
)
LazyRow(contentPadding = PaddingValues(top = 40.dp, start = 10.dp, bottom = 40.dp, end = 10.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)) {
itemsIndexed(list) { index, letter ->
Card(
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
) {
Text(
text = "$index $letter",
textAlign = TextAlign.Center,
fontSize = 20.sp,
modifier = Modifier
.fillMaxSize()
.wrapContentHeight(Alignment.CenterVertically)
)
}
}
}
到这里,基本上Lazy Layout 基本上覆盖到了,还有什么问题,可以到官网查询