前言

前段时间Compose发布了1.2.0beta版本,最大的变化之一莫过于LazyLayout去除了实验性标志。所以接下来,咱们不妨一起看看LazyGrid的用法 (嗯?这和上一句有关系吗)

LazyGrid包含两种微件:LazyVerticalGridLazyHorizontalGrid。两者内部均由LazyLayout实现(包括LazyColumnLazyRow也是由LazyLayout实现的)。不过今天我们不去考虑底层的LazyLayout,单纯着眼于Grid们

为行文方便,此处仅以LazyVerticalGrid为例。

基本使用

最简单的使用如下所示:

@Composable
fun SimpleLazyGrid(){
    LazyVerticalGrid(
        modifier = Modifier.fillMaxWidth(),
        // 固定两列
        columns = GridCells.Fixed(2) ,
        content = {
            items(12){
                RandomColorBox(modifier = Modifier.height(200.dp))
            }
        }
    )
}

其中用到的RandomColorBox仅仅是Box加上随机颜色的背景,唯一注意的是对于LazyLayout,因为涉及到重组过程,所以如果需要记住这个Color(重组时颜色不变),则需要使用rememberSaveable,其余不再赘述

上面的效果如下

image-20220521114045882

简单的网格布局就实现了

添加间隙

要为子元素之间添加空隙也很简单,指定一下arrangemntspacedBy即可

	//...
	horizontalArrangement = Arrangement.spacedBy(12.dp),
    verticalArrangement = Arrangement.spacedBy(8.dp)

效果如下

image-20220521115822198

当然也可以添加整体的外边距,设置contentPadding = PaddingValues()即可,如下:

contentPadding = PaddingValues(12.dp)

image-20220521115937301

适应大小

上述情况实际上会根据最大宽度来调整,在横屏状态下就可能会惨不忍睹(比如你加载有图片的情况)

所以除了固定列数外,还可以固定宽度,由Compose自动确定要放几列。这个也很简单,就是设置columns参数为Adaptive即可

// 固定宽度,自适应列数
columns = GridCells.Adaptive(200.dp) ,

效果如下:

横屏

image-20220521120428237

竖屏

image-20220521120535282

可以看到,我们指定的200.dp是最小值,由于能够容纳一个又无法容纳两个,Compose为我们自动调整为了只放一个,占满全部剩余宽度。

异形与自定义

某些元素占满全部宽度

itemitems均有span参数,设置此参数即可设定当前元素会占据几格

对于下面的代码:

// 固定列数
columns = GridCells.Fixed(3) ,
content = {
    item(span = {
        // 占据最大宽度
        GridItemSpan(maxLineSpan)
    }){
        RandomColorBox(modifier = Modifier.height(50.dp))
    }
    items(12){
        RandomColorBox(modifier = Modifier.height(200.dp))
    }
},

最上面那个元素就会占据一行,如下:

image-20220521121950200

上面用到的maxLineSpan即为当前行的最大Span,除此之外,还有另一个值maxCurrentLineSpan,二者之间关系如下

image-20220521124749563

更复杂的自定义

columns其实可以自定义,比方说,我们需要让一行中三个元素,宽度分别为1:2:1,那其实可以这样写。具体细节请参考下面的源码,返回的值即为各元素的宽度组成的List

// 自定义实现1:2:1
columns = object  : GridCells {
    override fun Density.calculateCrossAxisCellSizes(
        availableSize: Int,
        spacing: Int
    ): List<Int> {
        // 总共三个元素,所以其实两个间隔
        // |元素|间隔|元素|间隔|元素|
        // 计算一下所有元素占据的空间
        val availableSizeWithoutSpacing = availableSize - 2 * spacing
        // 小的两个大小即为剩余空间(总空间-间隔)/4
        val smallSize = availableSizeWithoutSpacing / 4
        // 大的那个就是除以2呗
        val largeSize = availableSizeWithoutSpacing / 2
        return listOf(smallSize, largeSize, smallSize)
    }
}

效果如下

image-20220521123719082

其余的效果大家就发挥想象啦

如果你想对排列方式也自定义,可以自己实现Arrangement.Vertical,视频中有给出例子(18:51左右)。这里感觉用处不大,不赘述了

一些提示 For LazyLayout

  1. 不要设置大小为0的控件

    这类问题主要在异步加载的场景中,可能加载之前你会将原本的大小设置为0(就是什么也没有)。在这种情况下,Compose在初始时将测量所有内容(因为他们高度为0,所以都在屏幕内),之后当数据加载完后,Compose又会重新重组。

    相反,你应当尽量保证数据加载前后item整体大小不变(如手动设置高度、使用placeholder等),以帮助LazyLayout正确计算哪些会被显示在屏幕上

  2. 避免嵌套同方向的可滚动微件

    避免使用如下代码(其实你这么用会直接报错)

    Column(modifier = Modifier.verticalScroll()) {
    	LazyColumn(/*这里不设置高度*/)
    }
    
    

    而应当改为:

    LazyColumn {
        item { Header() }
        items(){ }
        item{ Footer() }
    }
    
    
  3. 谨慎将多个子微件放到同一item

    即谨慎写出类似下面的代码

    LazyColumn {
        item {
            // 两个微件放在同一item里
            RandomColorBox(modifier = Modifier.size(40.dp))
            RandomColorBox(modifier = Modifier.size(40.dp))
        }
    }
    
    

    在这种情况下,Compose尽管可以按顺序渲染出这些子微件,但同一个item下的微件会被当作一个整体。如果某一部分可见,则其与部分也会被一起重组和绘制,可能会影响性能。在最严重情况下,整个LazyColumn仅包含一个item,那就完全失去了Lazy的特性。另一个问题是对于scrollToItem()这类方法,它们的index在计算时是按item而不是所有内部子元素排列的,也就是说,对于下面的例子,尽管总共有4个微件,但算index的时候只有0/1/2三个而已。

image-20220521163154088

​ 不过也有些情况倒是推荐这么用,比如在item中包含微件本身和Divider。一是二者本身语义上就相关联,Divider也不该影响index;二是Divider较小,不影响性能

  1. 使用Type

如果你的列表项有多种不同的类型,可以在itemitems方法中指定contentType,这有助于Compose在重组时选择相同Type的微件进行复用,可以提高性能。

contentType = { data.type }

Google画的饼

参考的视频中Google其实画了些饼

  1. 瀑布流布局正在开发中
  2. item添加和删除的动画也正在开发中

目前RecyclerView对应的瀑布流布局Compose中还没有对应实现,我试图用VerticalLazyGrid实现然并不行,它摆放的时候会确保每行高度一样……目前的开源库都是多个LazyColumn并排实现的伪效果。所以还是等吧

GitHub 加速计划 / compose / compose
39
5
下载
compose - Docker Compose是一个用于定义和运行多容器Docker应用程序的工具,通过Compose文件格式简化应用部署过程。
最近提交(Master分支:4 个月前 )
8f644eea Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com> 2 天前
56e92e34 Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com> 3 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐