鸿蒙学习实战之路:HarmonyOS 通用列表流程使用指南

HarmonyOS 通用列表流程使用指南

参考文档

  1. 华为开发者联盟 - 常见列表流开发实践

概述

列表流是采用以"行"为单位进行内容排列的布局形式,每"行"列表项通过文本、图片等不同形式的组合,高效地显示结构化的信息,当列表项内容超过屏幕大小时,可以提供滚动功能。

列表流具有以下特点:

  • 排版整齐
  • 重点突出
  • 对比方便
  • 浏览速度快

常见使用场景包括:应用首页、通讯录、音乐列表、购物清单等。

在 HarmonyOS 中,列表流主要使用 List 组件,按垂直方向线性排列子组件 ListItemGroup 或 ListItem,混合渲染任意数量的图文视图,从而构建列表内容。

基础组件

List 组件

List 是最基础的列表容器组件,用于创建可滚动的列表。

import { Column, List, ListItem, Text } from '@ohos/components';

@Entry
@Component
struct BasicListExample {
  private listData: string[] = ['列表项1', '列表项2', '列表项3', '列表项4', '列表项5'];

  build() {
    Column() {
      List({
        scroller: null,
        space: 20
      })
        .width('100%')
        .height('100%')
        .onScrollIndex((start: number, end: number) => {
          // 滚动事件处理
        })
        .onReachStart(() => {
          // 滚动到顶部
        })
        .onReachEnd(() => {
          // 滚动到底部,可用于加载更多
        })
        .onScrollStop(() => {
          // 滚动停止
        })
        {
          ForEach(this.listData, (item) => {
            ListItem() {
              Text(item)
                .width('100%')
                .height(60)
                .textAlign(TextAlign.Center)
                .fontSize(16)
            }
            .backgroundColor('#f0f0f0')
            .margin({ left: 16, right: 16 })
          }, item => item)
        }
    }
    .padding(16)
  }
}

常见列表流场景实现

1. 多类型列表项场景

场景描述

List 组件作为整个首页长列表的容器,通过 ListItem 对不同模块进行视图界面定制,常用于门户首页、商城首页等多类型视图展示的列表信息流场景。

实现原理

根据列表内部各部分视图对应数据类型的区别,渲染不同的 ListItem 子组件。

import { Column, List, ListItem, Text, Image, Grid, GridItem } from '@ohos/components';

@Entry
@Component
struct MultiTypeListExample {
  // 模拟数据
  private bannerData = {
    images: ['banner1.png', 'banner2.png'],
    type: 'banner'
  };

  private recommendData = [
    { id: 1, title: '推荐内容1', image: 'rec1.png' },
    { id: 2, title: '推荐内容2', image: 'rec2.png' },
    { id: 3, title: '推荐内容3', image: 'rec3.png' },
    { id: 4, title: '推荐内容4', image: 'rec4.png' },
  ];

  private articleData = [
    { id: 1, title: '文章标题1', desc: '文章描述1', type: 'article' },
    { id: 2, title: '文章标题2', desc: '文章描述2', type: 'article' },
  ];

  build() {
    Column() {
      List() {
        // Banner类型列表项
        ListItem() {
          Column() {
            Text('Banner区域')
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
            // 这里可以放置轮播图组件
            Image(this.bannerData.images[0])
              .width('100%')
              .height(200)
              .borderRadius(12)
          }
          .padding(16)
        }

        // 推荐内容类型列表项
        ListItem() {
          Column() {
            Text('推荐内容')
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
              .margin({ bottom: 16 })

            Grid() {
              ForEach(this.recommendData, (item) => {
                GridItem() {
                  Column() {
                    Image(item.image)
                      .width('100%')
                      .height(120)
                      .aspectRatio(1)
                    Text(item.title)
                      .fontSize(14)
                      .margin({ top: 8 })
                  }
                }
              }, item => item.id)
            }
            .columnsTemplate('1fr 1fr')
            .columnsGap(16)
            .rowsGap(16)
          }
          .padding(16)
        }

        // 文章列表类型列表项
        ListItem() {
          Column() {
            Text('最新文章')
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
              .margin({ bottom: 16 })

            ForEach(this.articleData, (item) => {
              Column() {
                Text(item.title)
                  .fontSize(16)
                  .fontWeight(FontWeight.Medium)
                Text(item.desc)
                  .fontSize(14)
                  .fontColor('#666666')
                  .margin({ top: 8 })
              }
              .padding(16)
              .backgroundColor('#f5f5f5')
              .borderRadius(8)
              .margin({ bottom: 12 })
            }, item => item.id)
          }
          .padding(16)
        }
      }
    }
  }
}

2. Tabs 吸顶场景

场景描述

当页面包含 Tabs 组件时,实现 Tabs 在滚动到顶部后保持吸顶状态,常用于分类浏览、数据筛选等场景。

实现原理

使用 List 组件的 sticky 属性实现元素吸顶效果。

import { Column, List, ListItem, Tabs, TabContent, Text } from '@ohos/components';

@Entry
@Component
struct StickyTabsExample {
  private tabList: string[] = ['推荐', '关注', '热门', '最新'];

  build() {
    Column() {
      // 页面顶部内容
      Column() {
        Text('页面标题')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .padding(16)
      }

      // 可吸顶的Tabs组件
      Tabs() {
        ForEach(this.tabList, (tab) => {
          TabContent() {
            List() {
              ListItem() {
                Text(`${tab}内容列表`)
                  .fontSize(16)
                  .padding(20)
              }
              // 生成更多列表项
              ForEach(Array.from({ length: 20 }), (_, index) => {
                ListItem() {
                  Text(`${tab}内容 ${index + 1}`)
                    .fontSize(16)
                    .padding(20)
                    .backgroundColor('#f5f5f5')
                    .margin({ left: 16, right: 16, top: 12 })
                    .borderRadius(8)
                }
              })
            }
          }
          .tabBar(tab)
        })
      }
      .width('100%')
      .height('80%')
      .barMode(BarMode.Scrollable)
      .scrollable(true)
    }
  }
}

3. 分组吸顶场景

场景描述

列表内容按分组展示,每个分组的标题在滚动到顶部时保持吸顶状态,常用于联系人列表、城市选择等场景。

实现原理

使用 ListItemGroup 组件创建分组,并设置 sticky 属性实现分组标题吸顶。

import { Column, List, ListItemGroup, ListItem, Text } from '@ohos/components';

@Entry
@Component
struct GroupStickyExample {
  // 模拟分组数据
  private groupData = [
    {
      title: 'A',
      items: ['Alice', 'Amy', 'Anna']
    },
    {
      title: 'B',
      items: ['Bob', 'Bill']
    },
    {
      title: 'C',
      items: ['Charlie', 'Cathy', 'Carol']
    }
  ];

  build() {
    Column() {
      Text('联系人列表')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .padding(16)

      List() {
        ForEach(this.groupData, (group) => {
          ListItemGroup({
            header: () => {
              // 分组标题
              return (
                Text(group.title)
                  .fontSize(18)
                  .fontWeight(FontWeight.Bold)
                  .backgroundColor('#e0e0e0')
                  .padding(12)
                  .width('100%')
              )
            },
            footer: () => {
              // 分组底部
              return null;
            },
            sticky: ListItemStickyStyle.Header
          })
          {
            // 分组内的列表项
            ForEach(group.items, (item) => {
              ListItem() {
                Text(item)
                  .fontSize(16)
                  .padding(16)
                  .width('100%')
              }
              .borderBottom({ width: 1, color: '#f0f0f0' })
            })
          }
        })
      }
      .width('100%')
      .height('80%')
    }
  }
}

4. 二级联动场景

场景描述

左侧为分类列表,右侧为对应分类下的内容列表,滚动右侧内容时左侧分类同步高亮显示,点击左侧分类时右侧内容滚动到对应位置,常用于商品分类、点餐应用等场景。

实现原理

通过监听左右两侧列表的滚动和点击事件,实现联动效果。

import { Column, Row, List, ListItem, Text, Scroll, ScrollController } from '@ohos/components';

@Entry
@Component
struct TwoLevelLinkageExample {
  private categories = ['分类1', '分类2', '分类3', '分类4', '分类5'];
  private selectedIndex: number = 0;
  private scrollController: ScrollController = new ScrollController();

  // 模拟分类内容数据
  private categoryData = {
    '分类1': ['商品1-1', '商品1-2', '商品1-3', '商品1-4'],
    '分类2': ['商品2-1', '商品2-2'],
    '分类3': ['商品3-1', '商品3-2', '商品3-3'],
    '分类4': ['商品4-1', '商品4-2', '商品4-3', '商品4-4', '商品4-5'],
    '分类5': ['商品5-1', '商品5-2']
  };

  // 点击分类处理函数
  onCategoryClick(index: number) {
    this.selectedIndex = index;
    // 滚动到对应位置
    this.scrollController.scrollTo({ xOffset: 0, yOffset: 0 });
  }

  build() {
    Column() {
      Row() {
        // 左侧分类列表
        List() {
          ForEach(this.categories, (category, index) => {
            ListItem() {
              Text(category)
                .fontSize(16)
                .padding(16)
                .width('100%')
                .backgroundColor(this.selectedIndex === index ? '#e6f7ff' : '#ffffff')
                .fontColor(this.selectedIndex === index ? '#1890ff' : '#000000')
                .onClick(() => this.onCategoryClick(index))
            }
            .borderBottom({ width: 1, color: '#f0f0f0' })
          })
        }
        .width('30%')
        .height('80%')

        // 右侧内容列表
        Scroll(this.scrollController) {
          Column() {
            ForEach(this.categoryData[this.categories[this.selectedIndex]], (item) => {
              Text(item)
                .fontSize(16)
                .padding(16)
                .width('100%')
                .backgroundColor('#f5f5f5')
                .margin({ bottom: 12 })
                .borderRadius(8)
            })
          }
          .padding(16)
        }
        .width('70%')
        .height('80%')
      }
    }
  }
}

性能优化

1. 虚拟化渲染

HarmonyOS 的 List 组件默认开启虚拟化渲染,只会渲染可视区域内的列表项,以及少量缓冲区的列表项,可以有效减少内存占用和提高渲染性能。

2. 懒加载

对于列表中的图片等资源,可以实现懒加载,只在图片即将进入可视区域时再加载。

import { Image } from '@ohos/components';

// 自定义懒加载图片组件
@Component
struct LazyLoadImage {
  @Prop src: string;
  private isLoaded: boolean = false;

  // 当组件可见时加载图片
  onVisibleChanged(visible: boolean) {
    if (visible && !this.isLoaded) {
      this.isLoaded = true;
    }
  }

  build() {
    Image(this.isLoaded ? this.src : '')
      .onVisible(this.onVisibleChanged)
      .width('100%')
      .height(200)
  }
}

3. 避免不必要的重渲染

使用memo@Watch等机制,避免组件不必要的重渲染。

// 使用memo优化子组件
const MemoizedListItem = memo((props: { data: any }) => {
  return (
    ListItem() {
      Text(props.data.title)
        .fontSize(16)
        .padding(16)
    }
  );
});

注意事项与最佳实践

1. 列表项高度设置

为了提高渲染性能,建议为列表项设置固定高度或最大高度,避免动态高度导致的布局重计算。

2. 大数据量处理

当列表数据量较大时:

  • 实现分页加载
  • 使用虚拟列表
  • 避免在滚动过程中执行复杂计算

3. 事件处理优化

  • 使用事件委托减少事件监听器数量
  • 避免在onScroll等频繁触发的事件中执行耗时操作
List()
  .onScroll(() => {
    // 避免在这里执行复杂计算
  })
  // 可以使用节流处理
  .onScrollThrottle(300, () => {
    // 每300ms最多执行一次
  });

4. 列表缓存

对于频繁访问的列表数据,可以实现缓存机制,减少重复请求。

常见问题解答

Q: List 组件和 Scroll 组件有什么区别?

A: List 组件专为列表数据展示优化,支持虚拟化渲染、分组、粘性头部等功能,性能更好;Scroll 组件是通用的滚动容器,功能更灵活但性能相对较差。

Q: 如何实现列表的下拉刷新和上拉加载更多?

A: 可以使用 Refresh 组件实现下拉刷新,结合 List 的 onReachEnd 事件实现上拉加载更多。

Refresh({
  refreshing: this.isRefreshing,
  onRefresh: () => {
    // 下拉刷新逻辑
    this.loadData();
  },
});
{
  List().onReachEnd(() => {
    // 上拉加载更多逻辑
    this.loadMoreData();
  });
}

Q: 如何优化长列表的性能?

A:

  1. 使用固定高度的列表项
  2. 开启虚拟化渲染
  3. 实现懒加载
  4. 避免在滚动过程中执行复杂操作
  5. 使用 memo 优化组件渲染

总结

HarmonyOS 的列表流开发主要使用 List 组件,可以实现多种复杂的列表场景。通过本文介绍的多类型列表项、Tabs 吸顶、分组吸顶和二级联动等场景的实现方法,开发者可以快速构建出功能丰富、性能优良的列表页面。在实际开发中,还需要注意性能优化和用户体验,确保列表滚动流畅,响应迅速。

posted @ 2025-12-15 20:46  时间煮鱼  阅读(1)  评论(0)    收藏  举报