Skip to content
🌊海洋蓝
🌸樱花粉
🍃森林绿
🔮幻夜紫
🌙暗夜黑

CommonListFlows — HarmonyOS List 组件四大经典场景实战解析

项目地址:https://gitcode.com/harmonyos_samples/CommonListFlows 技术栈:HarmonyOS / ArkTS / ArkUI

项目简介

在移动应用开发中,列表流是最常见、最核心的 UI 布局形式之一。无论是电商应用的商品列表、社交应用的信息流,还是工具类应用的城市选择器,都离不开列表组件的支撑。HarmonyOS 提供了强大的 List 组件,支持垂直方向线性排列子组件,能够高效渲染任意数量的图文视图。

CommonListFlows 是 HarmonyOS 官方示例项目,专注于展示基于 List 组件的四大经典列表流场景:

  • 多类型列表项场景:一个列表中混合展示轮播图、宫格、图文卡片等多种布局形式
  • Tab 吸顶场景:下滑时 Tab 栏固定在顶部,内容区域继续滚动
  • 分组吸顶场景:城市列表按字母分组,组标题随滚动吸顶,支持右侧字母导航
  • 二级联动场景:左侧导航与右侧内容联动,点击导航跳转对应内容,滚动内容高亮对应导航

本项目代码简洁、注释清晰,是学习和参考 HarmonyOS 列表开发的优质素材。

首页截图

上图展示了应用首页,四个按钮分别对应四大列表流场景。

功能概览

场景核心组件关键技术
多类型列表项List + ListItem + ListItemGroup下拉刷新、上拉加载、吸顶效果
Tab 吸顶Tabs + Scroll + ListnestedScroll 嵌套滚动、自定义 TabBar
分组吸顶List + ListItemGroup字母索引导航、分组吸顶
二级联动List + ListItemGroup双列表联动、滚动位置同步

工程结构

text
entry/src/main/ets
├── entryability
│   └── EntryAbility.ets              // 程序入口
├── entrybackupability
│   └── EntryBackupAbility.ets        // 备份能力
├── pages
│   ├── Index.ets                     // 首页(导航入口)
│   ├── HomePage.ets                  // 多类型列表项场景
│   ├── ManagerPage.ets               // Tab 吸顶场景
│   ├── CityList.ets                  // 分组吸顶场景
│   ├── CategoryPage.ets              // 二级联动场景
│   └── CustomListItem.ets            // 自定义列表项组件
└── model
    └── LinkDataModel.ets             // 二级联动场景数据模型

核心实现解析

场景一:多类型列表项(HomePage.ets)

多类型列表项场景模拟了一个典型的电商首页,在一个 List 组件中混合展示了轮播图、宫格入口、双列图片和景区分类列表四种不同的布局形式。

多类型列表项场景

上图展示了多类型列表项场景,包含搜索栏、轮播图、宫格入口、双列图片和景区列表。

核心代码:

typescript
List({ space: 12 }) {
  // 轮播图
  ListItem() {
    Swiper() {
      ForEach(this.swiperContent, (item: SwiperType) => {
        Stack({ alignContent: Alignment.BottomStart }) {
          Image($r(item.pic))
        }
      })
    }
    .autoPlay(true)
    .duration(1000)
    .indicator(new DotIndicator().selectedColor(Color.White))
  }

  // 宫格入口
  ListItem() {
    Grid() {
      ForEach(this.gridTitle, (item: Resource) => {
        GridItem() {
          Column() {
            Image($r('app.media.pic1'))
              .width(44).height(44).borderRadius(22)
            Text(item).fontSize(12)
          }
        }
      })
    }
    .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
  }

  // 景区分类列表(使用 ListItemGroup 实现分组吸顶)
  ForEach(this.scenicSpotTitle, (item: Resource) => {
    ListItemGroup({ header: this.scenicSpotHeader(item) }) {
      ForEach(this.scenicSpotArray, (scenicSpotItem: Resource) => {
        ListItem() {
          this.scenicSpotDetailBuilder(scenicSpotItem);
        }
      })
    }
  })
}
.sticky(StickyStyle.Header) // 分组标题吸顶
.onReachEnd(() => { // 上拉加载更多
  if (this.scenicSpotArray.length >= 20) {
    this.noMoreData = true;
    return;
  }
  setTimeout(() => {
    this.scenicSpotArray.push('scenic area' + (this.scenicSpotArray.length + 1));
  }, 500)
})

技术要点:

  1. 混合布局:通过 ListItem 包裹不同类型的子组件(SwiperGridColumn),实现一个列表中展示多种布局形式
  2. 下拉刷新:使用 Refresh 组件包裹 List,通过 onRefreshing 回调模拟网络请求
  3. 上拉加载:监听 onReachEnd 事件,在列表滚动到底部时自动加载更多数据
  4. 分组吸顶ListItemGroupheader 属性配合 sticky(StickyStyle.Header),实现景区分类标题吸顶效果

场景二:Tab 吸顶(ManagerPage.ets)

Tab 吸顶场景模拟了新闻类应用的常见布局:顶部搜索栏 + 内容区域 + 底部 TabBar。当用户下滑时,顶部的分类 Tab("关注"、"热搜"、"推荐"、"更多")会固定在屏幕顶部。

Tab 吸顶场景

上图展示了 Tab 吸顶场景,包含搜索栏、Banner、Tab 分类栏和底部 TabBar。

核心代码:

typescript
Stack({ alignContent: Alignment.Top }) {
  // 内容区域(可滚动)
  Scroll(this.scrollController) {
    Column() {
      Image($r('app.media.pic5')) // Banner 图
        .width('100%').height(186)

      // 自定义 TabBar
      Row({ space: 16 }) {
        ForEach(this.tabArray, (item: string, index: number) => {
          Text(item)
            .fontColor(this.currentTabIndex === index ? '#0a59f7' : Color.Black)
            .onClick(() => {
              this.contentTabController.changeIndex(index);
              this.currentTabIndex = index;
            })
        })
      }

      // Tabs 嵌套 List(实现 Tab 内容滚动)
      Tabs({ barPosition: BarPosition.Start, controller: this.contentTabController }) {
        TabContent() {
          List({ space: 10, scroller: this.listScroller }) {
            CustomListItem({ imgUrl: $r('app.media.pic1'), title: $r('app.string.manager_content') })
            // ... 更多列表项
          }
          .nestedScroll({
            scrollForward: NestedScrollMode.PARENT_FIRST,
            scrollBackward: NestedScrollMode.SELF_FIRST
          })
        }
        .tabBar('follow')
        // ... 其他 TabContent
      }
    }
  }

  // 顶部搜索栏(固定在 Stack 顶部)
  Row() {
    Image($r('app.media.app_icon')).width(30).height(30)
    Search({ placeholder: $r('app.string.want_search') })
      .width('100%').height(40).layoutWeight(1)
    Text($r('app.string.search'))
  }
  .width('100%').height(60)
}

技术要点:

  1. Stack 层级布局:使用 Stack 将搜索栏固定在顶部,内容区域在下方滚动
  2. 嵌套滚动nestedScroll 属性设置 PARENT_FIRSTSELF_FIRST,实现外层 Scroll 与内层 List 的协调滚动
  3. 自定义 TabBar:通过 Row + Text 实现自定义 TabBar,点击时切换 TabsController 的索引
  4. 底部导航:外层 Tabs 组件实现底部 TabBar 导航

场景三:分组吸顶(CityList.ets)

分组吸顶场景模拟了城市选择器,左侧是按字母分组的城市列表,右侧是字母导航栏。滑动城市列表时,组标题会吸顶显示;点击右侧字母,左侧列表会滚动到对应位置。

分组吸顶场景

上图展示了分组吸顶场景,包含当前城市、热门城市和按字母分组的城市列表,右侧有字母导航。

核心代码:

typescript
Stack({ alignContent: Alignment.End }) {
  // 城市列表
  List({ scroller: this.cityScroller }) {
    // 当前城市
    ListItemGroup({ header: this.itemHead($r('app.string.current_city')) }) {
      ListItem() { Text(this.currentCity) }
    }

    // 热门城市
    ListItemGroup({ header: this.itemHead($r('app.string.popular_cities')) }) {
      ForEach(this.hotCities, (item: string) => {
        ListItem() { this.textContent(item); }
      })
    }

    // 按字母分组的城市数据
    ForEach(this.groupWorldList, (item: string) => {
      ListItemGroup({ header: this.itemHead(item) }) {
        ForEach(this.getCitiesWithGroupName(item), (cityItem: City) => {
          ListItem() { this.textContent(cityItem.city); }
        })
      }
    })
  }
  .sticky(StickyStyle.Header) // 组标题吸顶
  .onScrollIndex((index: number) => {
    // 滚动时同步更新右侧导航高亮
    this.selectNavIndex = index - 2;
  })

  // 右侧字母导航
  Column() {
    List({ scroller: this.navListScroller }) {
      ForEach(this.groupWorldList, (item: string, index: number) => {
        ListItem() {
          Text(item)
            .fontColor(index === this.selectNavIndex ? '#FFFFFF' : Color.Black)
            .backgroundColor(index === this.selectNavIndex ? '#bEDAF2' : Color.Transparent)
            .onClick(() => {
              this.selectNavIndex = index;
              this.isClickScroll = true;
              // 点击字母导航,滚动城市列表到对应位置
              this.cityScroller.scrollToIndex(index + 2, false, ScrollAlign.START);
            })
        }
      })
    }
  }
  .width('10%')
}

技术要点:

  1. ListItemGroup 分组:使用 ListItemGroupheader 属性定义分组标题,配合 sticky 实现吸顶
  2. 字母导航联动:右侧字母列表点击时,通过 scrollToIndex 方法控制左侧城市列表滚动到对应位置
  3. 滚动同步:监听 onScrollIndex 事件,在左侧列表滚动时同步更新右侧导航的高亮状态
  4. 冲突处理:通过 isClickScroll 状态变量区分点击导航和手动滚动,避免两者冲突

场景四:二级联动(CategoryPage.ets)

二级联动场景模拟了电商分类页面,左侧是一级分类导航,右侧是对应的商品列表。点击左侧导航,右侧滚动到对应分类;滚动右侧内容,左侧对应导航高亮显示。

二级联动场景

上图展示了二级联动场景,左侧是分类导航(精选服饰高亮),右侧是对应的商品列表。

核心代码:

typescript
Row() {
  // 左侧分类导航
  List({ scroller: this.navTitleScroller }) {
    ForEach(this.categoryList, (item: NavTitleModel, index: number) => {
      ListItem() {
        Text(item.titleName)
          .fontSize(this.currentTitleId === index ? 16 : 14)
          .fontColor(this.currentTitleId === index ? '#0A59F7' : Color.Black)
          .fontWeight(this.currentTitleId === index ? FontWeight.Bold : FontWeight.Normal)
          .onClick(() => {
            // 点击左侧导航,右侧滚动到对应分类
            this.listChange(index, true);
          })
      }
    })
  }
  .width(100)

  // 右侧商品列表
  List({ scroller: this.goodsListScroller }) {
    ForEach(this.categoryList, (item: NavTitleModel) => {
      ListItemGroup({ space: 12, header: this.goodsHeaderBuilder(item.titleName) }) {
        ForEach(item.goodsList, (goodsItem: GoodsDataModel) => {
          ListItem() {
            Row() {
              Image(goodsItem.imgUrl).height('100%').aspectRatio(1)
              Column() {
                Text(goodsItem.goodsName).fontSize(14).maxLines(2)
                Text('¥' + goodsItem.price).fontSize(18).fontColor(Color.Red)
              }
              .layoutWeight(1)
            }
            .width('100%').height(96)
          }
        })
      }
    })
  }
  .sticky(StickyStyle.Header)
  .onScrollIndex((index: number) => {
    // 右侧滚动时,左侧导航高亮同步
    this.listChange(index, false)
  })
}

// 联动控制方法
listChange(index: number, isGoods: boolean) {
  if (this.currentTitleId !== index) {
    this.currentTitleId = index;
    if (isGoods) {
      // 点击左侧,右侧滚动
      this.goodsListScroller.scrollToIndex(index);
    } else {
      // 滚动右侧,左侧高亮
      this.navTitleScroller.scrollToIndex(index);
    }
  }
}

技术要点:

  1. 双列表布局:使用 Row 包裹左右两个 List,左侧固定宽度,右侧自适应
  2. 双向联动listChange 方法统一处理双向联动逻辑,通过 isGoods 参数区分触发来源
  3. 滚动同步:点击左侧时调用 goodsListScroller.scrollToIndex,滚动右侧时调用 navTitleScroller.scrollToIndex
  4. 视觉反馈:通过 currentTitleId 状态变量控制左侧导航的字体大小、颜色、粗细变化

数据模型(LinkDataModel.ets)

二级联动场景使用了简单的数据模型来组织分类和商品数据:

typescript
export interface GoodsDataModel {
  titleId: number,
  goodsId: number,
  goodsName: string,
  imgUrl: Resource,
  price: number,
}

export interface NavTitleModel {
  titleId: number,
  titleName: Resource,
  goodsList: GoodsDataModel[]
}

class GoodsViewModel {
  getLinkData(): NavTitleModel[] {
    let linkDataList: NavTitleModel[] = [];
    let titleId: number = 0;
    linkData.forEach((item: LinkDataModel) => {
      if (titleId !== item.titleId) {
        linkDataList.push({ titleId: item.titleId, titleName: item.titleName, goodsList: [] });
      }
      // 为每个分类生成 10 个商品
      for (let i = 1; i <= 10; i++) {
        linkDataList[linkDataList.length - 1].goodsList.push({
          titleId,
          goodsId: item.goodsId + i,
          goodsName: uiContext?.getHostContext()!.resourceManager.getStringSync(item.goodsName.id) + i,
          imgUrl: item.imgUrl,
          price: Number(i.toString() + item.price.toString())
        })
      }
      titleId = item.titleId;
    })
    return linkDataList;
  }
}

运行效果

以下是四个场景的实际运行截图:

多类型列表项Tab 吸顶
多类型列表项Tab 吸顶
分组吸顶二级联动
分组吸顶二级联动

总结

CommonListFlows 项目通过四个典型场景,全面展示了 HarmonyOS List 组件的核心能力和常用技巧:

  1. ListItemGroup + sticky:实现分组吸顶效果,适用于分类列表、城市选择器等场景
  2. nestedScroll:解决嵌套滚动冲突,实现 Tab 吸顶等复杂交互
  3. Scroller + scrollToIndex:实现列表位置控制和双向联动
  4. Refresh + onReachEnd:实现下拉刷新和上拉加载,完善列表交互体验

这些技术不仅适用于示例中的场景,也可以灵活应用到电商、社交、工具等各类应用的列表开发中。对于正在学习 HarmonyOS 开发的开发者来说,这是一个非常值得深入研究的示例项目。

参考资源

Released under the MIT License.