1# ListItemGroup 2 3<!--Kit: ArkUI--> 4<!--Subsystem: ArkUI--> 5<!--Owner: @yylong--> 6<!--Designer: @yylong--> 7<!--Tester: @liuzhenshuo--> 8<!--Adviser: @HelloCrease--> 9 10该组件用来展示列表item分组,宽度默认充满[List](ts-container-list.md)组件,必须配合List组件来使用。 11 12> **说明:** 13> 14> - 该组件从API version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 15> - 该组件的父组件只能是[List](ts-container-list.md)。 16> - ListItemGroup组件不支持设置[通用属性aspectRatio](ts-universal-attributes-layout-constraints.md#aspectratio)。 17> - 当ListItemGroup的父组件List的listDirection属性为Axis.Vertical时,设置[通用属性height](ts-universal-attributes-size.md#height)属性不生效。ListItemGroup的高度为header高度、footer高度和所有ListItem布局后总高度之和。 18> - 当父组件List的listDirection属性为Axis.Horizontal时,设置[通用属性width](ts-universal-attributes-size.md#width)属性不生效。ListItemGroup的宽度为header宽度、footer宽度和所有ListItem布局后总宽度之和。 19> - 当前ListItemGroup内部的ListItem组件不支持编辑、拖拽功能,即ListItem组件的editable属性不生效。 20> - ListItemGroup使用direction属性设置布局方向不生效,ListItemGroup组件布局方向跟随父容器List组件的布局方向。 21 22## 子组件 23 24包含[ListItem](ts-container-listitem.md)子组件。支持通过渲染控制类型([if/else](../../../ui/state-management/arkts-rendering-control-ifelse.md)、[ForEach](../../../ui/state-management/arkts-rendering-control-foreach.md)、[LazyForEach](../../../ui/state-management/arkts-rendering-control-lazyforeach.md)和[Repeat](../../../ui/state-management/arkts-new-rendering-control-repeat.md))动态生成子组件,更推荐使用LazyForEach或Repeat以优化性能。 25 26 27## 接口 28 29ListItemGroup(options?: ListItemGroupOptions) 30 31创建ListItemGroup组件。 32 33**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 34 35**系统能力:** SystemCapability.ArkUI.ArkUI.Full 36 37**参数:** 38 39| 参数名 | 类型 | 必填 | 说明 | 40| -------- | -------- | -------- | -------- | 41| options | [ListItemGroupOptions](#listitemgroupoptions对象说明)| 否 | 列表item分组组件参数。 | 42 43## ListItemGroupOptions对象说明 44 45ListItemGroup组件参数。 46 47**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 48 49**系统能力:** SystemCapability.ArkUI.ArkUI.Full 50 51| 名称 | 类型 | 只读 | 可选 | 说明 | 52| ------------------- | --------------------------------------------------- | ---- | -- | ------------------------------------------------------------ | 53| header | [CustomBuilder](ts-types.md#custombuilder8) | 否 | 是 | 设置ListItemGroup头部组件。<br/>**说明:**<br/>可以放单个子组件或不放子组件。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 54| headerComponent<sup>13+</sup> | [ComponentContent](../js-apis-arkui-ComponentContent.md) | 否 | 是 | 使用ComponentContent类型参数设置ListItemGroup头部组件。<br/>**说明:**<br/>可以放单个子组件或不放子组件。 该参数的优先级高于参数header。即同时设置header和headerComponent时,以headerComponent设置的值为准。<br/>同一个headerComponent不推荐同时给不同的ListItemGroup使用,否则会导致显示问题。<br/>**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。 | 55| footer | [CustomBuilder](ts-types.md#custombuilder8) | 否 | 是 | 设置ListItemGroup尾部组件。<br/>**说明:**<br/>可以放单个子组件或不放子组件。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 56| footerComponent<sup>13+</sup> | [ComponentContent](../js-apis-arkui-ComponentContent.md) | 否 | 是 | 使用ComponentContent类型参数设置ListItemGroup尾部组件。<br/>**说明:**<br/>可以放单个子组件或不放子组件。该参数的优先级高于参数footer。 即同时设置footer和footerComponent时,以footerComponent设置的值为准。<br/>同一个footerComponent不推荐同时给不同的ListItemGroup使用,否则会导致显示问题。<br/>**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。 | 57| space | number \| string | 否 | 是 | 列表项间距。只作用于ListItem与ListItem之间,不作用于header与ListItem、footer与ListItem之间。<br/>默认值:0<br/>单位:vp <br/>**说明:**<br/>设置为负数或者大于等于List内容区长度时,按默认值显示。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 58| style<sup>10+</sup> | [ListItemGroupStyle](#listitemgroupstyle10枚举说明) | 否 | 是 | 设置List组件卡片样式。<br/>默认值:ListItemGroupStyle.NONE<br/>设置为ListItemGroupStyle.NONE时无样式。<br/>设置为ListItemGroupStyle.CARD时,建议配合[ListItem](ts-container-listitem.md)的ListItemStyle.CARD同时使用,显示默认卡片样式。 <br/>卡片样式下,ListItemGroup默认规格:左右外边距12vp,上下左右内边距4vp。<br/>卡片样式下,为卡片内的列表选项提供了默认的focus、hover、press、selected和disable样式。<br/>**说明:**<br/>当前卡片模式下,使用默认Axis.Vertical排列方向,如果listDirection属性设置为Axis.Horizontal,会导致显示混乱;List属性alignListItem默认为ListItemAlign.Center,居中对齐显示。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 59 60## 属性 61 62### divider 63 64divider(value: [ListDividerOptions](ts-container-list.md#listdivideroptions18对象说明) | null) 65 66设置ListItem分割线样式,默认无分割线。 67 68strokeWidth, startMargin和endMargin不支持设置百分比。 69 70ListItem设置[多态样式](ts-universal-attributes-polymorphic-style.md)时,被按压的子组件上下的分割线不绘制。 71 72**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 73 74**系统能力:** SystemCapability.ArkUI.ArkUI.Full 75 76**参数:** 77 78| 参数名 | 类型 | 必填 | 说明 | 79| ------ | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 80| value | [ListDividerOptions](ts-container-list.md#listdivideroptions18对象说明) \| null | 是 | ListItem分割线样式。<br/> 默认值:null | 81 82### childrenMainSize<sup>12+</sup> 83 84childrenMainSize(value: ChildrenMainSize) 85 86设置ListItemGroup组件的子组件在主轴方向的大小信息。 87 88**说明:** 89> 90> - 必须同时给所在的List组件设置childrenMainSize属性才可以正常生效。 91 92**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 93 94**系统能力:** SystemCapability.ArkUI.ArkUI.Full 95 96**参数:** 97 98| 参数名 | 类型 | 必填 | 说明 | 99| ---------- | ------ | ---- | ------------------------------- | 100| value | [ChildrenMainSize](ts-container-scrollable-common.md#childrenmainsize12对象说明) | 是 | 该对象用来维护子组件在主轴方向的大小信息。| 101 102## ListItemGroupStyle<sup>10+</sup>枚举说明 103 104List组件卡片样式枚举。 105 106**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 107 108**系统能力:** SystemCapability.ArkUI.ArkUI.Full 109 110| 名称 | 值 | 说明 | 111| ---- | ---- | ------------------ | 112| NONE | 0 | 无样式。 | 113| CARD | 1 | 显示默认卡片样式。 | 114 115 116 117## 示例 118 119### 示例1(设置吸顶/吸底) 120 121该示例通过stick实现了Header吸顶和Footer吸底的效果。 122 123ListDataSource实现了LazyForEach数据源接口[IDataSource](ts-rendering-control-lazyforeach.md#idatasource),用于通过LazyForEach给List和ListItemGroup提供子组件。 124 125<!--code_no_check--> 126```ts 127// ListDataSource.ets 128export class TimeTableDataSource implements IDataSource { 129 private list: TimeTable[] = []; 130 private listeners: DataChangeListener[] = []; 131 132 constructor(list: TimeTable[]) { 133 this.list = list; 134 } 135 136 totalCount(): number { 137 return this.list.length; 138 } 139 140 getData(index: number): TimeTable { 141 return this.list[index]; 142 } 143 144 registerDataChangeListener(listener: DataChangeListener): void { 145 if (this.listeners.indexOf(listener) < 0) { 146 this.listeners.push(listener); 147 } 148 } 149 150 unregisterDataChangeListener(listener: DataChangeListener): void { 151 const pos = this.listeners.indexOf(listener); 152 if (pos >= 0) { 153 this.listeners.splice(pos, 1); 154 } 155 } 156 157 // 通知控制器数据变化 158 notifyDataChange(index: number): void { 159 this.listeners.forEach(listener => { 160 listener.onDataChange(index); 161 }); 162 } 163 164 // 修改第一个元素 165 public change1stItem(temp: TimeTable): void { 166 this.list[0] = temp; 167 this.notifyDataChange(0); 168 } 169} 170 171export class ProjectsDataSource implements IDataSource { 172 private list: string[] = []; 173 174 constructor(list: string[]) { 175 this.list = list; 176 } 177 178 totalCount(): number { 179 return this.list.length; 180 } 181 182 getData(index: number): string { 183 return this.list[index]; 184 } 185 186 registerDataChangeListener(listener: DataChangeListener): void { 187 } 188 189 unregisterDataChangeListener(listener: DataChangeListener): void { 190 } 191} 192 193export interface TimeTable { 194 title: string; 195 projects: string[]; 196} 197``` 198 199<!--code_no_check--> 200```ts 201// xxx.ets 202import { TimeTable, ProjectsDataSource, TimeTableDataSource } from './ListDataSource'; 203@Entry 204@Component 205struct ListItemGroupExample { 206 itemGroupArray: TimeTableDataSource = new TimeTableDataSource([]); 207 208 aboutToAppear(): void { 209 let timeTable: TimeTable[] = [ 210 { 211 title: '星期一', 212 projects: ['语文', '数学', '英语'] 213 }, 214 { 215 title: '星期二', 216 projects: ['物理', '化学', '生物'] 217 }, 218 { 219 title: '星期三', 220 projects: ['历史', '地理', '政治'] 221 }, 222 { 223 title: '星期四', 224 projects: ['美术', '音乐', '体育'] 225 } 226 ]; 227 this.itemGroupArray = new TimeTableDataSource(timeTable); 228 } 229 230 @Builder 231 itemHead(text: string) { 232 Text(text) 233 .fontSize(20) 234 .backgroundColor(0xAABBCC) 235 .width('100%') 236 .padding(10) 237 } 238 239 @Builder 240 itemFoot(num: number) { 241 Text('共' + num + '节课') 242 .fontSize(16) 243 .backgroundColor(0xAABBCC) 244 .width('100%') 245 .padding(5) 246 } 247 248 build() { 249 Column() { 250 List({ space: 20 }) { 251 LazyForEach(this.itemGroupArray, (item: TimeTable) => { 252 ListItemGroup({ header: this.itemHead(item.title), footer: this.itemFoot(item.projects.length) }) { 253 LazyForEach(new ProjectsDataSource(item.projects), (project: string) => { 254 ListItem() { 255 Text(project) 256 .width('100%') 257 .height(100) 258 .fontSize(20) 259 .textAlign(TextAlign.Center) 260 .backgroundColor(0xFFFFFF) 261 } 262 }, (item: string) => item) 263 } 264 .divider({ strokeWidth: 1, color: Color.Blue }) // 每行之间的分界线 265 }) 266 } 267 .width('90%') 268 .sticky(StickyStyle.Header | StickyStyle.Footer) 269 .scrollBar(BarState.Off) 270 }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 }) 271 } 272} 273``` 274 275 276 277### 示例2(设置卡片样式) 278 279该示例展示了ListItemGroup的卡片样式效果。 280 281```ts 282// xxx.ets 283@Entry 284@Component 285struct ListItemGroupExample2 { 286 private arr: ArrObject[] = [ 287 { 288 style: ListItemGroupStyle.CARD, 289 itemStyles: [ListItemStyle.CARD, ListItemStyle.CARD, ListItemStyle.CARD] 290 }, 291 { 292 style: ListItemGroupStyle.CARD, 293 itemStyles: [ListItemStyle.CARD, ListItemStyle.CARD, ListItemStyle.NONE] 294 }, 295 { 296 style: ListItemGroupStyle.CARD, 297 itemStyles: [ListItemStyle.CARD, ListItemStyle.NONE, ListItemStyle.CARD] 298 }, 299 { 300 style: ListItemGroupStyle.NONE, 301 itemStyles: [ListItemStyle.CARD, ListItemStyle.CARD, ListItemStyle.NONE] 302 } 303 ]; 304 305 build() { 306 Column() { 307 List({ space: '4vp', initialIndex: 0 }) { 308 ForEach(this.arr, (item: ArrObject, index?: number) => { 309 ListItemGroup({ style: item.style }) { 310 ForEach(item.itemStyles, (itemStyle: number, itemIndex?: number) => { 311 ListItem({ style: itemStyle }) { 312 if (index != undefined && itemIndex != undefined) { 313 Text('第' + (index + 1) + '个Group中第' + (itemIndex + 1) + '个item') 314 .width('100%') 315 .textAlign(TextAlign.Center) 316 } 317 } 318 }, (item: string) => item) 319 } 320 }) 321 } 322 .width('100%') 323 .multiSelectable(true) 324 .backgroundColor(0xDCDCDC) 325 } 326 .width('100%') 327 .padding({ top: 5 }) 328 } 329} 330 331interface ArrObject { 332 style: number; 333 itemStyles: number[]; 334} 335``` 336 337 338### 示例3(设置Header/Footer) 339 340该示例通过ComponentContent设置Header/Footer。 341 342<!--code_no_check--> 343```ts 344// xxx.ets 345import { ComponentContent } from '@kit.ArkUI'; 346import { TimeTable, ProjectsDataSource, TimeTableDataSource } from './ListDataSource'; 347 348class HeadBuilderParams { 349 text: string | Resource; 350 constructor(text: string | Resource) { 351 this.text = text; 352 } 353} 354 355class FootBuilderParams { 356 num: number | Resource; 357 constructor(num: number | Resource) { 358 this.num = num; 359 } 360} 361 362@Builder 363function itemHead(params: HeadBuilderParams) { 364 Text(params.text) 365 .fontSize(20) 366 .height('48vp') 367 .width('100%') 368 .padding(10) 369 .backgroundColor($r('sys.color.background_tertiary')) 370} 371 372@Builder 373function itemFoot(params: FootBuilderParams) { 374 Text('共' + params.num.toString() + '节课') 375 .fontSize(20) 376 .height('48vp') 377 .width('100%') 378 .padding(10) 379 .backgroundColor($r('sys.color.background_tertiary')) 380} 381 382@Component 383struct MyItemGroup { 384 item: TimeTable = { title: '', projects: [] }; 385 header?: ComponentContent<HeadBuilderParams> = undefined; 386 footer?: ComponentContent<FootBuilderParams> = undefined; 387 headerParam = new HeadBuilderParams(this.item.title); 388 footerParam = new FootBuilderParams(this.item.projects.length); 389 itemArr: ProjectsDataSource = new ProjectsDataSource([]); 390 391 aboutToAppear(): void { 392 this.header = new ComponentContent(this.getUIContext(), wrapBuilder(itemHead), this.headerParam); 393 this.footer = new ComponentContent(this.getUIContext(), wrapBuilder(itemFoot), this.footerParam); 394 this.itemArr = new ProjectsDataSource(this.item.projects); 395 } 396 GetHeader() { 397 this.header?.update(new HeadBuilderParams(this.item.title)); 398 return this.header; 399 } 400 401 GetFooter() { 402 this.footer?.update(new FootBuilderParams(this.item.projects.length)); 403 return this.footer; 404 } 405 406 build() { 407 ListItemGroup({ 408 headerComponent: this.GetHeader(), 409 footerComponent: this.GetFooter() 410 }) { 411 LazyForEach(this.itemArr, (project: string) => { 412 ListItem() { 413 Text(project) 414 .width('100%') 415 .height(100) 416 .fontSize(20) 417 .textAlign(TextAlign.Center) 418 } 419 }, (item: string) => item) 420 } 421 .divider({ strokeWidth: 1, color: Color.Blue }) // 每行之间的分界线 422 } 423} 424 425@Entry 426@Component 427struct ListItemGroupExample { 428 itemGroupArray: TimeTableDataSource = new TimeTableDataSource([]); 429 aboutToAppear(): void { 430 let timeTable: TimeTable[] = [ 431 { 432 title: '星期一', 433 projects: ['语文', '数学', '英语'] 434 }, 435 { 436 title: '星期二', 437 projects: ['物理', '化学', '生物'] 438 }, 439 { 440 title: '星期三', 441 projects: ['历史', '地理', '政治', '体育'] 442 }, 443 { 444 title: '星期四', 445 projects: ['美术', '音乐'] 446 } 447 ]; 448 this.itemGroupArray = new TimeTableDataSource(timeTable); 449 } 450 451 build() { 452 Column() { 453 Button('update').width(100).height(50).onClick(() => { 454 this.itemGroupArray.change1stItem({ 455 title: '更新后的星期一', 456 projects: ['语文', '物理', '历史', '美术'] 457 }); 458 }) 459 List({ space: 20 }) { 460 LazyForEach(this.itemGroupArray, (item: TimeTable) => { 461 MyItemGroup({ item: item }) 462 }, (item: TimeTable) => item.title) // LazyForEach依赖键值判断是否刷新子组件 463 } 464 .layoutWeight(1) 465 .sticky(StickyStyle.Header | StickyStyle.Footer) 466 .scrollBar(BarState.Off) 467 } 468 .backgroundColor($r('sys.color.background_primary')) 469 } 470} 471``` 472 473