Jetpack Compose 中的嵌套 LazyColumn

Jetpack Compose 中的嵌套 LazyColumn

在展示一组元素时,我们通常会使用 Column 和 Row。然而,当涉及到长列表的显示时,我们使用 LazyColumn、LazyRow 或 LazyGrids,这些组件仅渲染屏幕上可见的项目,从而提高性能并减少内存消耗。

在实现嵌套 LazyColumn 之前,让我们简要了解一些用于渲染大列表的主要组件。

LazyColumn 和 LazyRow

LazyColumn 用于垂直排列,而 LazyRow 用于水平排列。与 RecyclerView 类似,它们支持反向布局、滚动状态、方向调整、分隔符、多视图类型等功能。

LazyColumn {
        items(data) { item ->
            Box(
                modifier = Modifier
                    .height(100.dp)
                    .fillMaxWidth()
                    .background(Color.Magenta)
                    .padding(16.dp)
            )
            Spacer(modifier = Modifier.padding(8.dp))
        }
}


LazyRow {
        items(data) { item ->
            Box(
                modifier = Modifier
                    .width(100.dp)
                    .height(200.dp)
                    .background(Color.Magenta)
                    .padding(16.dp)
            )
            Spacer(modifier = Modifier.padding(8.dp))
        }
    }

LazyList 中的索引位置

LazyColumn 和 LazyRow 提供了 itemsIndexed 函数,使我们能够访问列表中每个项目的索引号。

LazyColumn {
      itemsIndexed(items = dataList) { index, data ->

          if (index == 0) {
              ... 
          }else{
            ....
          }
      }
  }

LazyList 的唯一 ID

LazyList 中的 key 参数确保列表中的每个项目都有一个稳定且唯一的键,这对于高效的列表更新和性能优化至关重要。

LazyColumn {
        items(items = allProductEntities, key = { item -> item.id }) { product ->
            ProductItem(product) {
                onProductClick(product.id.toString())
            }
        }
    }

多视图类型

如果我们想显示不同的视图类型,例如头部、尾部或具有不同 UI 表现的项目,可以使用索引或检查列表中的视图类型来相应地显示它们。

假设我们希望在列表的最顶部展示一个 HeroCard,然后显示其余的 API 数据。我们可以通过在 LazyColumn 中使用索引或 item 函数轻松实现这一点。
HeroCard & Other Items in LazyColumn

如下所示:

LazyColumn {
        itemsIndexed(items = dataList) { index, data ->

            if (index == 0) {
                HeroCard(data)
            } else {
                when (data.categoryType) {

                    CategoryType.Recent -> {
                        RecentItem(data) {
                            onRecentItemClick(data.id))
                        }
                    }

                    CategoryType.Popular -> {
                        PopularItem(data) {
                            onPopularItemClick(data.id))
                        }
                    }

                    else -> {
                        TrendingItem(data) {
                            onTrendingItemClick(data.id)
                        }
                    }

                }
            }
        }
    }

正如之前提到的,如果需要向列表中追加额外的项目或添加不同的组件,可以在 LazyList 中使用 item 函数,如下所示:

 LazyColumn {
        item {
            HeroCardItem()
        }
        items(data) { item ->
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
                    .background(Color.Magenta)
                    .padding(16.dp)
            )
            Spacer(modifier = Modifier.padding(8.dp))
        }
        item {
            FooterCardItem()
        }

 }

@Composable
fun HeroCardItem() {
    Column {
        Box(
            modifier = Modifier
                .height(500.dp)
                .fillMaxWidth()        
                .padding(16.dp)
        ){
           ...
        }
        Spacer(modifier = Modifier.padding(8.dp))
    }
}

@Composable
fun FooterCardItem() {
    Column {
        Box(
            modifier = Modifier
                .height(100.dp)
                .fillMaxWidth()
                .padding(16.dp)
        ){
           ...
        }
        Spacer(modifier = Modifier.padding(8.dp))
    }
}

这种方法使我们能够灵活地在列表中添加和排列不同的组件,同时保持高效的性能和内存管理。

II. LazyGrid

使用 LazyGrid 组件及其变体,如 LazyVerticalGrid、LazyHorizontalGrid 和 StaggeredGrid,我们可以轻松地利用惰性加载功能渲染项目。

定义网格中的行和列

我们可以使用以下属性来定义网格中的行和列:

使用 Adaptive

Adaptive 会根据内容和可用空间调整行或列的大小。

columns = GridCells.Adaptive(minSize = 128.dp)
rows = GridCells.Adaptive(minSize = 128.dp)

使用 FixedSize

FixedSize 为行或列指定一个固定的大小。

columns = GridCells.FixedSize(100.dp)
rows = GridCells.FixedSize(100.dp)

使用 Fixed

Fixed 设置一个固定数量的行或列。

columns = GridCells.Fixed(4)
rows = GridCells.Fixed(4)
columns = StaggeredGridCells.Fixed(2)

LazyVerticalGrid 示例

让我们看一个渲染 LazyVerticalGrid 的例子:

@Composable
fun ExampleVGrid(data: List<String>) {

    LazyVerticalGrid(
        columns = GridCells.Adaptive(minSize = 128.dp),
        contentPadding = PaddingValues(8.dp)
    ) {
        items(data.size) { index ->
            Card(
                modifier = Modifier
                    .padding(4.dp)
                    .fillMaxWidth(),
            ) {
                Text(
                    text = data[index],
                    fontWeight = FontWeight.Bold,
                    textAlign = TextAlign.Center,
                    modifier = Modifier.padding(16.dp)
                )
            }
        }
    }
}

III. Flow Layout

Flow layout 帮助我们以自然流动的方式排列元素。我们可以使用 FlowColumnFlowRow 分别进行垂直和水平排列。

注意:FlowRowFlowColumn 是实验性的。
其用法与LazyGrids类似。你可以在这里阅读相关内容。

好了,现在让我们开始实现嵌套懒加载列表。

嵌套 LazyList

通过在 LazyColumnLazyRow 组件中嵌套彼此,我们可以创建分层的 UI 布局,我们称之为 NestedLazyColumnNestedLazyRow

在这个例子中,我们使用 LazyColumn 作为主容器来垂直显示类别列表,而 LazyRow 则嵌套在 LazyColumn 的每一项中,用于水平显示故事卡片。

假设我们有一个 API,它返回所有类别及其事件。

{
  "categories": [
    {
      "name": "Recent",
      "events": [
        {
          "title": "Spring Music Festival",
          "organizer": "Music Events Inc.",
          "image": "spring_music_festival.jpg"
        },
        ....
      ]
    },
    {
      "name": "Popular",
      "events": [
        {
          "title": "Food Truck Rally",
          "organizer": "Local Food Association",
          "image": "food_truck_rally.jpg"
        },
        ...
      ]
    },
    ....
  ]
}

首先,我们为这个 JSON 创建一个数据类。可以使用 Gson Kotlin Serialization 来帮助我们解析数据。

data class Event(
    val title: String,
    val organizer: String,
    val image: String
)

data class CategoryWithEvents(
    val name: String,
    val events: List<Event>
)

按照仓库中的代码,我使用了 NetworkBoundResource 在一个函数中检索本地数据库和 API 数据。我们跳过这些细节,直接进入 UI 渲染部分。

通过以下代码,我们可以轻松创建这种类型的嵌套布局:

@Composable
fun NestedLazyColumnExample(allCategoryEvents: List<CategoryWithEvents>) {
    LazyColumn(
        state = listState
    ) {
        items(allCategoryEvents){ categoryEvents ->

            CategoryHeader(categoryEvents.categoryName)

            LazyRow {
                items(categoryEvents.event, 
                      key = { event -> event.id }){ event ->
                    
                    EventItem(data = event) {

                    }
                }
            }
        }
    }
}

@Composable
fun EventItem(event: List<Events>, onEventClick : (String) -> Unit){

Card(
      modifier = Modifier
          .padding(MaterialTheme.dimens.regular)
          .width(200.dp)
          .fillMaxHeight()
          .clickable {
              onEventClick(eventEntity.id.toString())
          },
      shape = MaterialTheme.shapes.medium
    ) {
        .....
      }
}

@Composable
fun CategoryHeader(title: String) {
    Text(text = title, modifier = Modifier.padding(9.dp))
}

完成后,我们的嵌套 LazyColumnLazyRow 将正常工作。
但是,如果我们嵌套 LazyColumn 会怎样呢?

LazyColumn(
    state = listState
) {
    items(allProductEntities) { allProducts ->
        ExploreHeader(allProducts.categoryName)
        LazyColumn {
            items(allProducts.products, key = { product -> product.id }) { product ->
                ExploreItem(productEntity = product) {

                }
            }
        }
    }
}

如果我们嵌套 LazyColumn 而不预定义嵌套列的高度,我们将会遇到以下错误:

java.lang.IllegalStateException: Vertically scrollable component was measured 
with an infinity maximum height constraints, which is disallowed. One of the common 
reasons is nesting layouts like LazyColumn and Column(Modifier.verticalScroll()). 
...

避免LazyColumn限制

为了解决 LazyColumn 的这一限制,可以使用以下几种技术:

1. 使用预定义或动态高度

我们可以为嵌套的可组合项定义高度。这样的方法效果不错,但嵌套列将具有固定高度,内容只能在该固定高度内滚动。

 LazyColumn(
        state = listState
    ) {
        items(allProductEntities) { allProducts ->
            ExploreHeader(allProducts.categoryName)

            LazyColumn(modifier = Modifier.height(550.dp)) {
                items(allProducts.products) { product ->
                    ExploreItem(productEntity = product) {

                    }
                }
            }
        }
    }

有些开发人员会估算嵌套列的动态高度,他们创建逻辑来确定 LazyColumn 的动态高度,不过我对这种方法的实用性持保留态度。

2. 使用 Column 替换 LazyColumn

LazyColumn 替换为 Column 可能会导致失去项目的懒加载功能,从而影响列表性能,使其不那么高效。

allEvents.events.forEach { event ->
    Column {
        EventItem(eventEntity = event) {
            // 处理事件
        }
    }
}

3. 使用 LazyListScope

目前,这是渲染嵌套LazyColumn时最有效的方法。我们使用 LazyListScope 来创建一个懒加载列项。
这样,嵌套项目也会被懒加载。

fun LazyListScope.EventItem(
    eventList: List<Event>,
) {
    items(eventList) { eventData ->
        // 渲染事件数据
    }
}

接下来,让我们创建上述的 NestedLazyColumn:

@Composable
fun ExploreList(allEventCategories: List<CategoryWithEvents>, onEventClick: (String) -> Unit) {
    ExploreContent(allEventCategories, onEventClick)
}

@Composable
fun ExploreContent(allEventCategories: List<CategoryWithEvents>, onEventClick: (String) -> Unit) {
    val listState = rememberLazyListState()
    LazyColumn(
        state = listState
    ) {
        allEventCategories.map { (categoryName, eventList) ->
            stickyHeader {
                ExploreHeader(categoryName)
            }
            EventItem(eventList, onEventClick)
        }
    }
}

// LazyListScope Item

fun LazyListScope.EventItem(
    eventList: List<Event>,
    onEventClick: (String) -> Unit
) {
    items(eventList) { eventData ->
        Card(
            modifier = Modifier
                .padding(MaterialTheme.dimens.regular)
                .fillMaxWidth()
                .fillMaxHeight()
                .clickable {
                    onEventClick(eventData.title)
                },
            shape = MaterialTheme.shapes.medium
        ) {
            Column(
                Modifier.fillMaxWidth(),
            ) {
                AsyncImage(
                    model = eventData.image,
                    contentDescription = eventData.title,
                    modifier = Modifier
                        .background(MaterialTheme.colorScheme.secondaryContainer)
                        .fillMaxWidth()
                        .height(150.dp),
                    contentScale = ContentScale.Crop,
                )

                Column(
                    Modifier.padding(10.dp),
                ) {
                    Text(
                        text = eventData.title,
                        style = appTypography.bodyMedium,
                        maxLines = 1,
                        color = MaterialTheme.colorScheme.onTertiaryContainer,
                        modifier = Modifier.padding(8.dp)
                    )
                    // Other UI...
                    Spacer(modifier = Modifier.height(8.dp))
                }
            }
        }
    }
}

完成后,我们的 NestedLazyColumn 看起来不错,并且运行良好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/726940.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL 四、mysql进阶 4(索引的数据结构)

一、为什么使用索引 以及 索引的优缺点 1.为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构&#xff0c;就好比一本教科书的目录部分&#xff0c;通过目录中找到对应文章的页码&#xff0c;便可快速定位到需要的文章。Mysql中也是一样的道理&#xff0c;进行数…

DY-110DP低电压继电器 25-124V 嵌入式安装 约瑟JOSEF

系列型号 DY-110电压继电器&#xff1b;GY-110电压继电器&#xff1b; GDY-110电压继电器&#xff1b;DY-110/AC电压继电器&#xff1b; GY-110/AC电压继电器&#xff1b;GDY-110/AC电压继电器&#xff1b; DL-110电压继电器&#xff1b;GL-110电压继电器&#xff1b; DL-…

汇编程序入门指南

什么是机器语言&#xff1f; 机器语言就是由二进制数字构成的程序&#xff0c;CPU 可以直接对其解释、执行。 汇编语言、C 语言、Java、BASIC 等编程语言编写的程序&#xff0c;也都需要先转换成机器语言才能被执行。机器语言有时也叫作“原生代码”&#xff08;Native Code&…

6V升12V2.5A芯片 升压恒压IC 惠海H6392 低功耗,高效率,高性价比

H6392是一款适用于2.6-5V输入电压范围的升压DC-DC转换器&#xff0c;具有多种优点&#xff0c;如高效率、低功耗、高精度和高性价比。 这款芯片具有多个显著特点&#xff0c;包括输出可调至12V、可调过电流保护范围为1.2~2.5A、内置18V耐压MOS等。其低待机功耗小于0.1uA&#x…

一种稀疏贝叶斯学习的旋转机械故障诊断方法(MATLAB)

轴承的故障诊断技术是通过检测轴承故障特征信息来判断轴承的具体故障为位置或损伤程度。在轴承发生损坏时&#xff0c;故障特征信息会随着工作时间的增长变得明显。轴承的损坏过程可以分为四个阶段。第一个阶段为损伤初始阶段&#xff0c;轴承故障特征信号一般无法测量。第二个…

redis-基础篇(1)

黑马redis-基础篇笔记 1. 初识redis REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统&#xff0c;是跨平台的非关系型数据库。Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的…

Vue3 【仿 react 的 hook】封装 useTitle

效果预览 页码加载时&#xff0c;自动获取网页标题通过input输入框&#xff0c;可以实时改变网页标题 代码实现 index.vue <template><h1>网页的标题为&#xff1a; {{ titleRef }}</h1><p>通过input输入框实时改变网页的标题 <input v-model"…

一个软件是如何开发出来的呢?

一、前言 如今&#xff0c;AI大爆发的时代&#xff0c;作为一名IT从业者&#xff0c;你是否也想尝试开发一套自己的系统&#xff0c;实现那些看似有可能实现的天马行空的想法&#xff0c;变成一个优秀甚至伟大的产品&#xff0c;甚至带来某个行业的革新&#xff0c;那作为一名…

【保姆级教程】Linux 基于 Docker 部署 MySQL 和 Nacos 并配置两者连接

一、Linux 部署 Docker 1.1 卸载旧版本&#xff08;如有&#xff09; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine1.2 安装 yum-utils 包 sudo yum install -y…

配电室数据中心巡检3d可视化搭建的详细步骤

要搭建配电室巡检的3D可视化系统&#xff0c;可以按照以下步骤进行&#xff1a; 收集配电室数据&#xff1a; 首先&#xff0c;需要收集配电室的相关数据&#xff0c;包括配电室的布局、设备信息、传感器数据等。可以通过实地调查、测量和设备手册等方式获取数据。 创建3D模型…

GRIT论文阅读笔记

一篇试图统一生成任务和编码任务的工作&#xff0c;就是把只能完成生成任务的GPT改成既能生成又能encode。思路其实很简单&#xff0c;就是在输入的时候添加instruction tokens来指引模型做representation还是generation&#xff0c;然后各自算损失。representation任务用的是d…

JSON序列化与反序列化

目录 JSON序列化 查看JSON文件&#xff0c;设置数据模板类 ​编辑 Newtonsoft.Json下载 运行结果展示 JSON反序列化 序列化是将对象或数据结构转换为可以存储或传输的格式&#xff08;如JSON字符串&#xff09;的过程&#xff0c;而反序列化则是将这个格式的数据转换回原…

海豚调度监控:使用图关系网络解决核心链路告警,减轻任务运维负担!

&#x1f4a1; 本系列文章是 DolphinScheduler 由浅入深的教程&#xff0c;涵盖搭建、二开迭代、核心原理解读、运维和管理等一系列内容。适用于想对 DolphinScheduler了解或想要加深理解的读者。 祝开卷有益。大数据学习指南 大家好&#xff0c;我是小陶&#xff0c;之前分享了…

Centos7.9安装openldap

文章目录 一、背景二、正文2.1 openldap服务端必要软件安装2.1.1使用yum命令安装2.1.2安装libdb相关依赖2.1.3复制一个默认配置到指定目录下&#xff0c;并授权&#xff0c;这一步一定要做&#xff0c;然后再启动服务&#xff0c;不然生成密码时会报错2.1.4授权给ldap用户&…

【第13章】进阶调试思路:如何安装复杂节点IP-Adapter?(安装/复杂报错/节点详情页/精读)ComfyUI基础入门教程

🎈背景 IP-Adapter这个名字,大家可能听说过,可以让生成的结果从参考图中学习人物、画风的一致性,在目前是比较实用的一个节点,广泛的用于照片绘制、电商作图等方面。 但同时,这个节点也是比较难安装的一个节点。 所以,这节课,我们就通过一个案例,来学习如何在Comf…

电子期刊制作教程:跟着步骤轻松学会制作

随着数字时代的快速发展&#xff0c;电子期刊以其独特的便捷性和互动性&#xff0c;已经成为信息传播的重要载体。你是否也想掌握制作电子期刊的技能呢&#xff1f;今天&#xff0c;就让我来为你一步步解析电子期刊的制作过程&#xff0c;带你轻松学会制作属于自己的电子期刊。…

地瓜网络技术综合助手教你一键下载腾讯会议高清视频

当您错过腾讯会议的直播课程&#xff0c;不必担心&#xff0c;地瓜网络技术综合助手帮您轻松获取视频回放。 只需几个简单步骤&#xff0c;即可在手头保留珍贵的学习资料。 首先&#xff0c;启动地瓜网络技术综合助手&#xff0c; 进行软件初始化并开启监测功能。 接下来&…

PyTorch中实现Transformer模型

前言 对于论文给出的模型架构&#xff0c;使用 PyTorch 分别实现各个部分。 命名transformer.py&#xff0c;引入的相关库函数&#xff1a; import copy import torch import math from torch import nn from torch.nn.functional import log_softmax# module: 需要深拷贝的模…

DLS Markets:日本银行豪赌美债巨亏,危机是否重演?

摘要 日本第五大银行Norinchukin Bank农林中央金库宣布大规模抛售美债&#xff0c;以弥补因利率上升导致的巨额损失。这一决定引发市场关注&#xff0c;担忧是否会引发一场美债“清仓甩卖”。本文详细分析了Norinchukin的背景、抛售美债的原因及其对全球金融市场的潜在影响。 …

2024年,计算机相关专业依旧是热门选择吗?未来趋势大揭秘!

文章目录 引言一、行业竞争现状二、专业与个人的匹配度判断三、专业前景分析总结 引言 在科技日新月异的今天&#xff0c;计算机专业一直以其强大的实用性和广阔的就业前景吸引着无数学子的目光。然而&#xff0c;随着人工智能、大数据、云计算等领域的飞速发展&#xff0c;我…