HarmonyOS鸿蒙Next中Scroll、List等滚动组件可以在item中间添加分隔符吗?

HarmonyOS鸿蒙Next中Scroll、List等滚动组件可以在item中间添加分隔符吗? 比如在不同的年份、月份中间添加一个分隔条组件。

不做数据分组的话,分隔条没有停靠效果是吗?

11 回复

开发者你好,针对您的问题: 1、可以在 item 中间加分隔符吗? List:可以设置分割线,比如:

List() {
  //...
}
.divider({ color: Color.Blue, strokeWidth: 1 })

Scroll:可以加多个Divider组件。 2、不做数据分组,分隔条没有停靠效果是吗? 是的,不分组(不用 ListItemGroup): List:自定义分隔条随滚动走,不停靠。 Scroll:完全没有原生停靠能力,要停靠就必须分组。


您还可以参考我的这篇实战文章:

通过ListItemGroup实现列表分组展示及吸顶吸底效果

https://developer.huawei.com/consumer/cn/blog/topic/03216343276443038

更多关于HarmonyOS鸿蒙Next中Scroll、List等滚动组件可以在item中间添加分隔符吗?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


divider

divider(value: ListDividerOptions | null)

设置ListItem分割线样式,默认无分割线。

List的分割线画在主轴方向两个子组件之间,第一个子组件上方和最后一个子组件下方不会绘制分割线。

多列模式下,ListItem与ListItem之间的分割线起始边距从每一列的交叉轴方向起始边开始计算,单列模式从List交叉轴方向起始边开始计算。

ListItem设置多态样式时,被按压的子组件上下的分割线不绘制。

卡片能力: 从API version 9开始,该接口支持在ArkTS卡片中使用。

元服务API: 从API version 11开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

参数名 类型 必填 说明
value ListDividerOptions | null ListItem分割线样式。

默认值:null

分组效果代码:

// xxx.ets
class TimeTableDataSource implements IDataSource {
  private list: TimeTable[] = [];
  constructor(list: TimeTable[]) {
    this.list = list;
  }
  totalCount(): number {
    return this.list.length;
  }
  getData(index: number): TimeTable {
    return this.list[index];
  }
  registerDataChangeListener(listener: DataChangeListener): void {
  }
  unregisterDataChangeListener(listener: DataChangeListener): void {
  }
}
class ProjectsDataSource implements IDataSource {
  private list: string[] = [];
  constructor(list: string[]) {
    this.list = list;
  }
  totalCount(): number {
    return this.list.length;
  }
  getData(index: number): string {
    return this.list[index];
  }
  registerDataChangeListener(listener: DataChangeListener): void {
  }
  unregisterDataChangeListener(listener: DataChangeListener): void {
  }
}
@Entry
@Component
struct ListItemGroupExample {
  private timeTable: TimeTable[] = [
  {
    title: '星期一',
    projects: ['语文', '数学', '英语']
  },
  {
    title: '星期二',
    projects: ['物理', '化学', '生物']
  },
  {
    title: '星期三',
    projects: ['历史', '地理', '政治']
  },
  {
    title: '星期四',
    projects: ['美术', '音乐', '体育']
  }
];
  private scroller: ListScroller = new ListScroller();
  @State listIndexInfo: VisibleListContentInfo = { index: -1 };
  @State mess:string = 'null';
  @State itemBackgroundColorArr: boolean[] = [false];
  @Builder
  itemHead(text: string) {
    Text(text)
      .fontSize(20)
      .backgroundColor(0xAABBCC)
      .width('100%')
      .padding(10)
  }
  @Builder
  itemFoot(num: number) {
    Text('共' + num + '节课')
      .fontSize(16)
      .backgroundColor(0xAABBCC)
      .width('100%')
      .padding(5)
  }
  build() {
    Column() {
      List({ space: 20, scroller: this.scroller}) {
        LazyForEach(new TimeTableDataSource(this.timeTable), (item: TimeTable, index: number) => {
          ListItemGroup({ header: this.itemHead(item.title), footer: this.itemFoot(item.projects.length) }) {
            LazyForEach(new ProjectsDataSource(item.projects), (project: string, subIndex: number) => {
              ListItem() {
                Text(project)
                  .width('100%')
                  .height(100)
                  .fontSize(20)
                  .textAlign(TextAlign.Center)
                  .backgroundColor(this.itemBackgroundColorArr[index * 3 +subIndex] ? 0x68B4FF: 0xFFFFFF)
              }
            }, (item: string) => item)
          }
          .divider({ strokeWidth: 1, color: Color.Blue }) // 每行之间的分界线
        },(item: string) => item)
      }
      .width('90%')
      .sticky(StickyStyle.Header | StickyStyle.Footer)
      .scrollBar(BarState.Off)
      .gesture(
        PanGesture()
          .onActionUpdate((event: GestureEvent) => {
            if (event.fingerList[0] != undefined && event.fingerList[0].localX != undefined && event.fingerList[0].localY != undefined) {
              try {
                this.listIndexInfo =
                  this.scroller.getVisibleListContentInfo(event.fingerList[0].localX, event.fingerList[0].localY);
              } catch (error) {
                console.info('Failed to get visible list content info:', error);
              }
              let itemIndex:string = 'undefined';
              if (this.listIndexInfo.itemIndexInGroup != undefined ) {
                itemIndex = this.listIndexInfo.itemIndexInGroup.toString();
                if (this.listIndexInfo.index != undefined && this.listIndexInfo.index >= 0 &&
                  this.listIndexInfo.itemIndexInGroup >= 0 ) {
                  this.itemBackgroundColorArr[this.listIndexInfo.index * 3 + this.listIndexInfo.itemIndexInGroup] = true;
                }
              }
              this.mess = 'index:' + this.listIndexInfo.index.toString() + ' itemIndex:' + itemIndex;
            }
          }))
      .gesture(
        TapGesture({ count: 1 })
          .onAction((event: GestureEvent) => {
            if (event) {
              this.itemBackgroundColorArr.splice(0,this.itemBackgroundColorArr.length);
            }
          })
      )
      Text('您当前位置Item索引为'+ this.mess)
        .fontColor(Color.Red)
        .height(50)
    }.width('100%').height('90%').backgroundColor(0xDCDCDC).padding({ top: 5 })
  }
}
interface TimeTable {
  title: string;
  projects: string[];
}

效果图:

cke_9638.png

背景知识:

楼主,List有单独的设置分隔线的属性(.divider()),scroll没有单独设置分割线的属性,需要手动判断。

问题解决:

示例代码:

@Builder
function showScrollOrList(){
    Column(){
        Text("List 分隔")
            .fontSize(20)
            .fontColor(Color.Black)
            .fontWeight(FontWeight.Bold)
            .textAlign(TextAlign.Center)
        List() {
            ForEach([1,2,3], (item:number,index:number) => {
                ListItem() {
                    Text('条目 '+ item).padding(16)
                        .width("100%")
                        .fontSize(16)
                }
            })
        }
        .divider({
            strokeWidth: 1,        // 线宽
            color: '#eee',          // 颜色
            startMargin: 16,        // 左边距
            endMargin: 16           // 右边距
        })

        Text("Scroll 分隔")
            .fontSize(20)
            .fontColor(Color.Black)
            .fontWeight(FontWeight.Bold)
            .textAlign(TextAlign.Center)
        Scroll() {
            Column() {
                ForEach([1,2,3], (item:number, index) => {
                    Column() {
                        Text('条目 '+ item).padding(16)
                            .width("100%")
                            .fontSize(16)
                        // 最后一个 item 不画分割线
                        if (index < 2) {
                            Divider()
                                .strokeWidth(1)
                                .color('#eee')
                                .margin({ left: 16 })
                        }
                    }
                })
            }
        }

    }
    .width("100%")
    .height("100%")

}

真机演示:

cke_16835.png

  1. 可以在不同的年份、月份中间添加一个分隔条组件。数据做好flag,条件渲染就行。
  2. 关于停靠效果。
    · List还是推荐ListItemGroup+sticky。《示例》
    · Scroll和List都可以通过覆盖一个固定组件在顶部,通过监听滚动,根据数据项滚动位置控制覆盖组件的状态和显示。

首先第一个问题肯定可以添加分隔符的,后面我会说2种方式。然后是第二个问题不做数据分组的话,分隔条确实没有停靠效果。

  1. 简单分割线:使用List的divider属性

List组件内置了divider属性,可以在ListItem之间添加统一的分割线。但这种方式只能添加样式固定的线条,不支持自定义组件,也无法在不同位置显示不同样式的分隔符。

List({ space: 20 }) {
  // ListItem...
}
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 20, endMargin: 20 })
  1. 自定义分隔条组件:使用ListItemGroup的header/footer

使用ListItemGroup组件,将每个分组的数据包裹在一个ListItemGroup中,通过header属性设置分隔条组件。

// 定义分组数据模型
interface DataGroup {
  title: string;   // 分组标题,如"2024年6月"
  items: string[]; // 该分组下的数据项
}

@Entry
@Component
struct GroupListExample {
  private dataGroups: DataGroup[] = [
    { title: '2024年6月', items: ['事项1', '事项2', '事项3'] },
    { title: '2024年5月', items: ['事项4', '事项5'] },
    { title: '2024年4月', items: ['事项6', '事项7', '事项8', '事项9'] },
  ];

  // 分组分隔条(header)
  @Builder
  groupHeader(title: string) {
    Row() {
      Text(title)
        .fontSize(16)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height(40)
    .padding({ left: 16 })
    .backgroundColor('#F5F5F5')
  }

  build() {
    List({ space: 0 }) {
      ForEach(this.dataGroups, (group: DataGroup) => {
        ListItemGroup({ header: this.groupHeader(group.title) }) {
          ForEach(group.items, (item: string) => {
            ListItem() {
              Text(item)
                .width('100%')
                .height(60)
                .fontSize(15)
                .padding({ left: 16 })
            }
          }, (item: string) => item)
        }
        .divider({ strokeWidth: 0.5, color: '#EEEEEE' }) // 组内item之间的分割线
      }, (group: DataGroup) => group.title)
    }
    .width('100%')
    .height('100%')
    .sticky(StickyStyle.Header) // 启用header吸顶
  }
}

可以的。

无论是 Scroll + ForEach 还是 List + ListItem,本质上都可以把分隔条当成一个普通组件插进去,

直接渲染:

ForEach(data, (item) => {
  if (item.type === 'divider') {
    DividerView()
  } else {
    ItemView(item)
  }
})

即可。


但你说的“不做数据分组的话,分隔条没有停靠效果是吗?”

应该是对的。

普通情况下:

Item
Item
Divider
Item
Item

这个 Divider 只是列表里的一个普通元素。

滚过去就滚过去了:

↑
Divider
↓

不会自动吸顶。


如果想实现:

2024年    ← 吸顶
Item
Item
Item

滚到:

2025年    ← 替换2024年吸顶
Item
Item

这种效果,就需要使用 List 的分组能力。

通常会用:

List() {
  ForEach(groups, (group) => {
    ListItemGroup({
      header: HeaderView(group.title)
    }) {
      ...
    }
  })
}

然后开启:

.sticky(StickyStyle.Header)

或者对应的吸顶配置。

这样 Header 才会被系统识别成分组头,实现停靠效果。


所以简单总结:

  • 只想显示分隔条 → 直接插组件即可。
  • 想要年月分隔并吸顶 → 用 ListItemGroup + Header
  • Scroll + ForEach 没有原生吸顶能力,需要自己监听滚动实现。
  • List 做年月分组场景是官方更推荐的方案。

你可以直接使用 Divider 这个组件啊

不做数据分组的话,可以用条件渲染自定义组件,就是没有吸顶的效果,但是支持自定义分隔

interface DataItem {
    id: string;
    month: string;   // 所属月份,用于判断是否需要显示分隔条
    content: string;  // 列表项内容
}

@Entry
@Component
struct TesPage {
    private dataList: DataItem[] = [
        { id: '1',  month: '2026年1月', content: '完成项目立项评审' },
        { id: '2',  month: '2026年1月', content: '需求分析文档定稿' },
        { id: '3',  month: '2026年1月', content: '技术方案评审通过' },
        { id: '4',  month: '2026年2月', content: '核心模块开发完成' },
        { id: '5',  month: '2026年2月', content: '接口联调测试通过' },
        { id: '6',  month: '2026年3月', content: '系统测试完成' },
        { id: '7',  month: '2026年3月', content: '性能优化与安全加固' },
        { id: '8',  month: '2026年3月', content: '正式发布上线' },
        { id: '9',  month: '2026年4月', content: '系统测试完成' },
        { id: '10',  month: '2026年4月', content: '性能优化与安全加固' },
        { id: '11',  month: '2026年4月', content: '正式发布上线' },
        { id: '12',  month: '2026年5月', content: '系统测试完成' },
        { id: '13',  month: '2026年5月', content: '性能优化与安全加固' },
        { id: '14',  month: '2026年6月', content: '正式发布上线' },
        { id: '15',  month: '2026年7月', content: '系统测试完成' },
        { id: '16',  month: '2026年7月', content: '性能优化与安全加固' },
        { id: '17',  month: '2026年8月', content: '正式发布上线' },
        { id: '18',  month: '2026年9月', content: '系统测试完成' },
        { id: '19',  month: '2026年10月', content: '性能优化与安全加固' },
        { id: '20',  month: '2026年10月', content: '正式发布上线' },
    ];
    
    needSeparator(index: number): boolean {
        if (index === 0) {
            return true;
        }
        return this.dataList[index].month !== this.dataList[index - 1].month;
    }

    build() {
        Column() {
            Text('项目进度')
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
                .margin({ top: 20, bottom: 12 })

            List({ space: 0 }) {
                ForEach(this.dataList, (item: DataItem, index: number) => {
                    ListItem() {
                        Column() {
                            if (this.needSeparator(index)) {
                                Row() {
                                    Text(item.month)
                                        .fontSize(14)
                                        .fontColor('#666666')
                                }
                                .width('100%')
                                .height(36)
                                .padding({ left: 16 })
                                .backgroundColor('#F0F0F0')
                                .justifyContent(FlexAlign.Start)
                                .alignItems(VerticalAlign.Center)
                            }

                            Row() {
                                Text(item.content)
                                    .fontSize(16)
                                    .fontColor('#333333')

                                Blank()

                                Text('>')
                                    .fontSize(14)
                                    .fontColor('#CCCCCC')
                            }
                            .width('100%')
                            .height(56)
                            .padding({ left: 16, right: 16 })
                            .alignItems(VerticalAlign.Center)
                        }
                    }
                }, (item: DataItem) => item.id)
            }
            .divider({ strokeWidth: 0.5, color: '#E0E0E0', startMargin: 16, endMargin: 0 })
            .width('100%')
            .layoutWeight(1)
        }
        .width('100%')
        .height('100%')
        .backgroundColor('#FFFFFF')
    }
}

如果只是普通分隔线,List 里可以用 divider 或手动插入一个 ListItem;Scroll 里也只能手动放分隔组件。若你希望“年份/月”这种分隔条在滚动时有停靠效果,建议从数据层就做分组,用 ListItemGroup 的 header 来承载年月标题。手动插入的分隔条本质上只是普通 item,会跟着内容滚走,不会自动 sticky。Scroll 没有 List 那种分组/停靠语义,除非你自己监听滚动位置,再用一个覆盖层显示当前分组标题。

可以。在HarmonyOS鸿蒙Next中,通过List组件的divider属性设置分隔符样式。Scroll组件可手动在子项间插入Divider组件或自定义分隔线。List配合ListItemGroup时,也可利用itemDivider属性实现。

在 HarmonyOS Next 中,可以通过在 List 或 Scroll 中插入一个仅用于分隔的普通 item(比如一个高度较小的 Row/Text 组件),实现不同数据分组之间的视觉分隔。
但因为它是普通 item,滚动时不会停在顶部,不做数据分组的话,分隔条没有停靠(sticky)效果
如需分隔条具有粘性标题的效果(例如年份滚动时停留在顶部),必须使用 ListItemGroup 进行显式分组,并对组头设置 .sticky(StickyStyle.Header) 属性,这样组头才会在滚动时停留。

回到顶部