文章目录
- 前言
 - LazyListScope作用域 用来干什么?
 - LazyColumn组件含义?
 - LazyColumn的基本使用
 - LazyColumn Padding设置边距
 - LazyColumn 设置边距 (contentPadding)
 - LazyColumn 为每个子项设置边距 (Arrangement.spacedBy())
 - LazyColumn 根据 rememberLazyListState 记录item位置
 - 根据 items函数 新增 一个key参数,增加和删除操作时,提高页面性能问题
 
- LazyRow的含义
 - LazyRow的使用
 
- 总结
 
前言
现在应用市场上很多产品都少不了列表展示需求场景,例如通讯录,短信,音乐列表等等。
所以本篇文章讲解的组件 - 列表 (LazyList 和 LazyRow )
在了解 LazyList 和 LazyRow 之前,我们先了解下 LazyListScope作用域
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组件含义?
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的基本使用
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)
            )
        }
    }
}
 
LazyColumn Padding设置边距
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)
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()即可
LazyColumn 为每个子项设置边距 (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)
		            )
		        }
    }
}
 
效果图如下:
 
LazyColumn 根据 rememberLazyListState 记录item位置
有时候有这样的需求:让组件随着列表的滚动进行一些额外的响应。如随着滚动隐藏和显示某个组件
这时候 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")
            }
        }
    }
}
 

根据 items函数 新增 一个key参数,增加和删除操作时,提高页面性能问题
因为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的含义
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的使用
val list = ('A'..'Z').map { it.toString() }
LazyRow(state = state,
	   contentPadding = PaddingValues(top = 40.dp, start = 10.dp, bottom = 40.dp, end = 10.dp),
	   horizontalArrangement = Arrangement.spacedBy(20.dp)) {
	   itemsIndexed(list) { index, letter ->
	       Card(
	           modifier = Modifier
	               .height(300.dp)
	               .width(120.dp)
	       ) {
	           Text(
	               text = "$index $letter",
	               textAlign = TextAlign.Center,
	               fontSize = 20.sp,
	               modifier = Modifier
	                   .fillMaxSize()
	                   .wrapContentHeight(Alignment.CenterVertically)
	           )
	       }
	   }
}
 
到这里,基本上Lazy Layout 基本上覆盖到了,还有什么问题,欢迎反馈和评论区留言
总结
- LazyListScope作用域提供了 item , items(Int) , item(List) , itemsIndexed(List) 扩展函数来展示列表内容
 - LazyColumn 就是一个纵向滚动列表
 - contentPadding 设置列表组件的整个内容的边距
 - Arrangement.spacedBy() 为列表的每个子项设置边距
 - LazyColumn 根据 rememberLazyListState 记录第一个可见子项元素位置
 - items函数 新增 一个key参数,设置唯一性,增加和删除操作时,性能得到优化和提高
 - LazyRow 是一个横向布局的列表组件,用法和 LazyColumn一致
 


















