横向列表LazyRow
我们知道,Android View中的RecyclerView,可以通过设置其LayoutManager的方向来实现横向列表。Compose中用LazyRow来实现。
val dataList = arrayListOf<Int>()
for (index in 0 .. 10) { // 数据源
dataList.add(index)
}
LazyRow { // 横向列表
items(dataList) { data ->
Text(text = "$data")
}
}
能看到0-10的数字横向展示。从代码上看,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的逻辑
content: LazyListScope.() -> Unit // 描述内容的代码块
) {
// 省略...
}
可以看到,除了horizontalArrangement和verticalAlignment参数外,别的参数基本一致。
默认情况下,每个列表项的状态均与该项在列表中的位置相对应。但是数据集如果发生变化,可能会导致问题出现,因为位置发生变化的列表项实际上会丢失所有记忆状态。比如LazyColumn中的LazyRow,如果某个LazyRow更改的项位置,用户将丢失在该行内的滚动位置。为了避免这种情况,可以为每个列表项提供稳定的唯一键,为key参数提供一个块,提供稳定的键可以使项状态在数据集发生更改后还保持一致。我们看个例子:
val dataList = arrayListOf<Int>()
for (index in 0 .. 10) {
dataList.add(index)
}
LazyRow {
items(
items = dataList,
key = { index ->
index
}
) { data ->
Text(text = "$data")
}
}
我们在之前的例子中添加了项键key,类型是Any,所以我们可以传任何值,但需要注意的是,我们提供的参数类型必须是Bundle支持的类型。
网格列表LazyVerticalGrid
我们打开手机相册或者朋友圈里的图片,都能看到是以网格的列表形式展示。在Android View中的RecyclerView展示网格布局也是设置LayoutManager为GridLayoutManger。在Compose中用LazyVerticalGrid实现这种效果。
val photos = arrayListOf<Int>()
for(index in 0 .. 20) { // 添加21张图片
photos.add(R.drawable.ic_launcher_background)
}
LazyVerticalGrid(
cells = GridCells.Adaptive(minSize = 60.dp)) {
items(photos) { photo ->
Image(
painter = painterResource(id = photo),
contentDescription = "",
modifier = Modifier.padding(2.dp)
)
}
}
我们可以看到上图的网格列表效果。
我们看看它的源码:
@ExperimentalFoundationApi
@Composable
fun LazyVerticalGrid(
cells: GridCells, // 描述单元格如何形成列
modifier: Modifier = Modifier, // 描述符
state: LazyListState = rememberLazyListState(), // 它和LazyRow一致
contentPadding: PaddingValues = PaddingValues(0.dp), // 内边距
content: LazyGridScope.() -> Unit // 内容代码块
) {
// 省略...
}
我们可以看到有注解ExperimentalFoundationApi修饰,表示是实验性的API,我们发现里面有两个陌生的参数,第一个是cells和最后一个LazyGridScope。
描述单元格如何形成列————cells
cells类型是GridCells,用来描述单元格如何形成列,而且这个参数没有默认值,也不能为空,看看它的源码:
@ExperimentalFoundationApi
sealed class GridCells {
@ExperimentalFoundationApi
class Fixed(val count: Int) : GridCells()
@ExperimentalFoundationApi
class Adaptive(val minSize: Dp) : GridCells()
}
它是一个密封类,而且有两个子类:Fixed和Adaptive。从这两个子类命名大致知道他们的意义,Fixed是固定的列数;Adaptive是设置最小宽度并进行自适应布局。先卡看Fixed的用法,它有一个int类型的参数,可以生成具有固定数量行或列的单元格。
val photos = arrayListOf<Int>()
for(index in 0 .. 20) { // 添加21张图片
photos.add(R.drawable.ic_launcher_background)
}
LazyVerticalGrid(
cells = GridCells.Fixed(count = 5)) { // 5列
items(photos) { photo ->
Image(
painter = painterResource(id = photo),
contentDescription = "",
modifier = Modifier.padding(2.dp)
)
}
}
可以看到实际展示的效果就是5列展示的。Adaptive就是最开始例子代码中设置的,可以生成具有适应性的行数或列数的单元格,它尝试在每个单元格至少具有minSize空间和所有额外空间均匀分布的条件下,尽可能多地定位行或列。
LazyGridScope
content参数表示单元格的内容,类型是LazyGridScope。之前介绍lazyColumn的时候说过,它不接收@Composable内容块参数,而是接收一个LazyGridScope块。LazyVerticalGrid也不接收@Composable内容块参数,而是提供了一个LazyGridScope块。该块也提供了DSL,允许应用程序描述列表项内容,之后LazyVerticalGrid就和LazyColumn一样负责按照布局和滚动位置的要求添加每个列表项的内容。先看看LazyGridScope的源码:
@ExperimentalFoundationApi
interface LazyGridScope {
fun item(content: @Composable LazyItemScope.() -> Unit)
fun items(count: Int, itemContent: @Composable LazyItemScope.(index: Int) -> Unit)
}
其实这么一看,LazyGridScope和LazyListScope一样,都是接口,而且连方法都一样,只是类的实现不同,再看看LazyGridScope的扩展函数有哪些:
@ExperimentalFoundationApi
inline fun <T> LazyGridScope.items(
items: List<T>,
crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit
) = items(items.size) {
itemContent(items[it])
}
@ExperimentalFoundationApi
inline fun <T> LazyGridScope.itemsIndexed(
items: List<T>,
crossinline itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
) = items(items.size) {
itemContent(it, items[it])
}
@ExperimentalFoundationApi
inline fun <T> LazyGridScope.items(
items: Array<T>,
crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit
) = items(items.size) {
itemContent(items[it])
}
@ExperimentalFoundationApi
inline fun <T> LazyGridScope.itemsIndexed(
items: Array<T>,
crossinline itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
) = items(items.size) {
itemContent(it, items[it])
}
LazyGridScope的扩展方法和LazyListScope一模一样,所以使用方法也一模一样。上面使用了List构建LazyVerticalGrid,我们看看Array的方式来构建一个LazyVerticalGrid。
LazyVerticalGrid(
cells = GridCells.Fixed(count = 3)) {
items(photos.toArray()) { photo ->
Image(
painter = painterResource(id = photo as Int),
contentDescription = "",
modifier = Modifier.padding(2.dp)
)
}
}
可以看到,列表变为了3列展示,Array也可以正常使用。是不是很简单。好了,今天学习到这。相关代码已上传github: github.com/Licarey/com…