1# 栅格布局 (GridRow/GridCol) 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @zju_ljz--> 5<!--Designer: @lanshouren--> 6<!--Tester: @liuli0427--> 7<!--Adviser: @HelloCrease--> 8 9 10## 概述 11 12栅格布局是一种通用的辅助定位工具,对移动设备的界面设计有较好的借鉴作用。主要优势包括: 13 141. 提供可循的规律:栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题。通过将页面划分为等宽的列数和行数,可以方便地对页面元素进行定位和排版。 15 162. 统一的定位标注:栅格布局可以为系统提供一种统一的定位标注,保证不同设备上各个模块的布局一致性。这可以减少设计和开发的复杂度,提高工作效率。 17 183. 灵活的间距调整方法:栅格布局可以提供一种灵活的间距调整方法,满足特殊场景布局调整的需求。通过调整列与列之间和行与行之间的间距,可以控制整个页面的排版效果。 19 204. 自动换行和自适应:栅格布局可以完成一对多布局的自动换行和自适应。当页面元素的数量超出了一行或一列的容量时,他们会自动换到下一行或下一列,并且在不同的设备上自适应排版,使得页面布局更加灵活和适应性强。 21 22[GridRow](../reference/apis-arkui/arkui-ts/ts-container-gridrow.md)为栅格容器组件,需与栅格子组件[GridCol](../reference/apis-arkui/arkui-ts/ts-container-gridcol.md)在栅格布局场景中联合使用。 23 24 25## 栅格容器GridRow 26 27 28### 栅格容器断点 29 30栅格容器以设备的水平宽度([屏幕密度像素值](../reference/apis-arkui/arkui-ts/ts-pixel-units.md),单位vp)作为断点依据,定义设备的宽度类型,形成了一套断点规则。开发者可根据需求在不同的断点区间实现不同的页面布局效果。 31 32栅格容器默认断点将设备宽度分为xs、sm、md、lg四类,尺寸范围如下: 33 34| 断点名称 | 取值范围(vp) | 设备描述 | 35| ---- | --------------- | --------- | 36| xs | [0, 320) | 最小宽度类型设备。 | 37| sm | [320, 600) | 小宽度类型设备。 | 38| md | [600, 840) | 中等宽度类型设备。 | 39| lg | [840, +∞) | 大宽度类型设备。 | 40 41在GridRow栅格组件中,允许开发者使用[BreakPoints](../reference/apis-arkui/arkui-ts/ts-container-gridrow.md#breakpoints)自定义修改断点的取值范围,最多支持6个断点,除了默认的4个断点外,还可以启用xl和xxl断点,支持6种不同尺寸(xs,sm,md,lg,xl,xxl)设备的布局设置。 42 43| 断点名称 | 设备描述 | 44| ---- | --------- | 45| xs | 最小宽度类型设备。 | 46| sm | 小宽度类型设备。 | 47| md | 中等宽度类型设备。 | 48| lg | 大宽度类型设备。 | 49| xl | 特大宽度类型设备。 | 50| xxl | 超大宽度类型设备。 | 51 52- 开发者可根据实际使用场景,通过一个单调递增数组设置断点位置。由于栅格容器默认支持4个断点,在不设置断点位置时,系统为默认断点配置的单调递增数组为["320vp", "600vp", "840vp"]。开发者使用[BreakPoints](../reference/apis-arkui/arkui-ts/ts-container-gridrow.md#breakpoints)最多可支持6个断点,因此此单调递增数组最大长度为5。 53 54 假设传入的数组是[n0, n1, n2, n3, n4],则各个断点取值如下: 55 56 |断点|取值范围| 57 |---|-----------| 58 |xs |[0, n0) | 59 |sm |[n0, n1) | 60 |md |[n1, n2) | 61 |lg |[n2, n3) | 62 |xl |[n3, n4) | 63 |xxl|[n4, INF) | 64 65 ```ts 66 breakpoints: {value: ['100vp', '200vp']} // 表示xs、sm、md共3个断点被使用,小于100vp为xs,100vp-200vp为sm,大于200vp为md。 67 breakpoints: {value: ['320vp', '600vp']} // 表示xs、sm、md共3个断点被使用,小于320vp为xs,320vp-600vp为sm,大于600vp为md。 68 breakpoints: {value: ['320vp', '600vp', '840vp', '1440vp']} // 表示xs、sm、md、lg、xl共5个断点被使用,小于320vp为xs,320vp-600vp为sm, 600vp-840vp为md,840vp-1440vp为lg,大于1440vp为xl。 69 ``` 70 71- 栅格容器通过监听窗口或容器的尺寸变化进行断点,通过reference设置断点切换参考物。考虑到应用可能以非全屏窗口的形式显示,以应用窗口宽度为参照物更为通用。 72 73 例如,通过断点设置将应用宽度分成6个区间,通过columns配置各断点下栅格容器的栅格列数。 74 75 76 ```ts 77 // xxx.ets 78 @Entry 79 @Component 80 struct Index { 81 @State bgColors: ResourceColor[] = 82 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 83 'rgb(255,192,0)', 'rgb(170,10,33)']; 84 build() { 85 GridRow({ 86 columns: { 87 xs: 2, // 窗口宽度落入xs断点上,栅格容器分为2列。 88 sm: 4, // 窗口宽度落入sm断点上,栅格容器分为4列。 89 md: 8, // 窗口宽度落入md断点上,栅格容器分为8列。 90 lg: 12, // 窗口宽度落入lg断点上,栅格容器分为12列。 91 xl: 12, // 窗口宽度落入xl断点上,栅格容器分为12列。 92 xxl: 12 // 窗口宽度落入xxl断点上,栅格容器分为12列。 93 }, 94 breakpoints: { 95 value: ['320vp', '600vp', '840vp', '1440vp', '1600vp'], // 表示在保留默认断点['320vp', '600vp', '840vp']的同时自定义增加'1440vp', '1600vp'的断点,实际开发中需要根据实际使用场景,合理设置断点值实现一次开发多端适配。 96 reference: BreakpointsReference.WindowSize 97 } 98 }) { 99 ForEach(this.bgColors, (color:ResourceColor, index?:number|undefined) => { 100 GridCol({ span: 1 }) { // 所有子组件占一列。 101 Row() { 102 Text(`${index}`) 103 }.width("100%").height('50vp') 104 }.backgroundColor(color) 105 }) 106 } 107 } 108 } 109 ``` 110 111  112 113 114### 布局的总列数 115 116GridRow中通过columns设置栅格布局的总列数。 117 118- API version 20之前,columns默认值为12,即在未设置columns时,任何断点下,栅格布局均被分成12列。 119- API version 20及以后,columns默认值为{ xs: 2, sm: 4, md: 8, lg: 12, xl: 12, xxl: 12 }。 120 121 122 ```ts 123 // xxx.ets 124 @Entry 125 @Component 126 struct Index { 127 @State bgColors: ResourceColor[] = 128 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 129 'rgb(255,192,0)', 'rgb(170,10,33)', 'rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)']; 130 build() { 131 GridRow() { 132 ForEach(this.bgColors, (item:ResourceColor, index?:number|undefined) => { 133 GridCol({span: 1}) { 134 Row() { 135 Text(`${index}`) 136 }.width('100%').height('50') 137 }.backgroundColor(item) 138 }) 139 } 140 } 141 } 142 ``` 143 144 API version 20之前布局显示: 145 146  147 148 API version 20及以后布局显示(以sm设备为例,默认栅格列数为4): 149 150  151 152 153columns支持number和[GridRowColumnOption](../reference/apis-arkui/arkui-ts/ts-container-gridrow.md#gridrowcolumnoption)两种类型, 可按两种方式设置栅格布局的总列数。 154- 当columns类型为number时,栅格布局在任何尺寸设备下都被分为同一列数。下面分别设置栅格布局列数为4和8,子元素占一列,效果如下: 155 156 ```ts 157 // xxx.ets 158 @Entry 159 @Component 160 struct Index { 161 @State bgColors: ResourceColor[] = 162 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 163 'rgb(255,192,0)', 'rgb(170,10,33)']; 164 @State currentBp: string = 'unknown'; 165 build() { 166 Row() { 167 GridRow({ columns: 4 }) { 168 ForEach(this.bgColors, (item: ResourceColor, index?: number | undefined) => { 169 GridCol({ span: 1 }) { 170 Row() { 171 Text(`${index}`) 172 }.width('100%').height('50') 173 }.backgroundColor(item) 174 }) 175 } 176 .width('100%').height('100%') 177 } 178 .height(160) 179 .border({ color: 'rgb(39,135,217)', width: 2 }) 180 .width('90%') 181 } 182 } 183 ``` 184 185 ```ts 186 // xxx.ets 187 @Entry 188 @Component 189 struct Index { 190 @State bgColors: ResourceColor[] = 191 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 192 'rgb(255,192,0)', 'rgb(170,10,33)']; 193 @State currentBp: string = 'unknown'; 194 build() { 195 Row() { 196 GridRow({ columns: 8 }) { 197 ForEach(this.bgColors, (item: ResourceColor, index?: number | undefined) => { 198 GridCol({ span: 1 }) { 199 Row() { 200 Text(`${index}`) 201 }.width('100%').height('50') 202 }.backgroundColor(item) 203 }) 204 } 205 .width('100%').height('100%') 206 } 207 .height(160) 208 .border({ color: 'rgb(39,135,217)', width: 2 }) 209 .width('90%') 210 } 211 } 212 ``` 213 214  215 216- 当columns类型为[GridRowColumnOption](../reference/apis-arkui/arkui-ts/ts-container-gridrow.md#gridrowcolumnoption)时,支持下面6种不同尺寸(xs,sm,md,lg,xl,xxl)设备的栅格列数设置,不同尺寸的设备支持配置不同的栅格列数。 217 218 ```ts 219 // xxx.ets 220 @Entry 221 @Component 222 struct Index { 223 @State bgColors: ResourceColor[] = 224 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 225 'rgb(255,192,0)', 'rgb(170,10,33)']; 226 build() { 227 GridRow({ 228 columns: { sm: 4, md: 8 }, 229 breakpoints: { 230 value: ['320vp', '600vp', '840vp', '1440vp', '1600vp'] // 表示在保留默认断点['320vp', '600vp', '840vp']的同时自定义增加'1440vp', '1600vp'的断点,实际开发中需要根据实际使用场景,合理设置断点值实现一次开发多端适配。 231 } 232 }) { 233 ForEach(this.bgColors, (item: ResourceColor, index?: number | undefined) => { 234 GridCol({ span: 1 }) { 235 Row() { 236 Text(`${index}`) 237 }.width('100%').height('50') 238 }.backgroundColor(item) 239 }) 240 } 241 .height(200) 242 .border({ color: 'rgb(39,135,217)', width: 2 }) 243 } 244 } 245 ``` 246 API version 20之前布局显示(xs设备未配置栅格列数,取默认列数12): 247 248  249 250 API version 20及以后布局显示(xs设备继承sm设备栅格列数): 251 252  253 254 仅部分设置sm、md的栅格列数,未配置的xs、lg、xl、xxl设备根据[栅格列数补全](../reference/apis-arkui/arkui-ts/ts-container-gridrow.md#gridrowcolumnoption)取默认值。 255 256 257### 排列方向 258 259栅格布局中,可以通过设置GridRow的direction属性来指定栅格子组件在栅格容器中的排列方向。该属性可以设置为GridRowDirection.Row(从左往右排列)或GridRowDirection.RowReverse(从右往左排列),以满足不同的布局需求。通过合理的direction属性设置,可以使得页面布局更加灵活和符合设计要求。 260 261- 子组件默认从左往右排列。 262 263 264 ```ts 265 GridRow({ direction: GridRowDirection.Row }){ /* ... */ } 266 ``` 267 268  269 270- 子组件从右往左排列。 271 272 273 ```ts 274 GridRow({ direction: GridRowDirection.RowReverse }){ /* ... */ } 275 ``` 276 277  278 279 280### 子组件间距 281 282GridRow中通过gutter属性设置子元素在水平和垂直方向的间距。 283 284- 当gutter类型为number时,同时设置栅格子组件间水平和垂直方向边距且相等。下例中,设置子组件水平与垂直方向距离相邻元素的间距为10。 285 286 287 ```ts 288 GridRow({ gutter: 10 }){ /* ... */ } 289 ``` 290 291  292 293- 当gutter类型为GutterOption时,单独设置栅格子组件水平垂直边距,x属性为水平方向间距,y为垂直方向间距。 294 295 296 ```ts 297 GridRow({ gutter: { x: 20, y: 50 } }){ /* ... */ } 298 ``` 299 300  301 302 303## 子组件GridCol 304 305GridCol组件作为GridRow组件的子组件,通过给GridCol传参或者设置属性两种方式,设置span(占用列数),offset(偏移列数),order(元素序号)的值。 306 307- 设置span。 308 309 310 ```ts 311 let Gspan:Record<string,number> = { 'xs': 1, 'sm': 2, 'md': 3, 'lg': 4 } 312 GridCol({ span: 2 }){} 313 GridCol({ span: { xs: 1, sm: 2, md: 3, lg: 4 } }){} 314 GridCol(){}.span(2) 315 GridCol(){}.span(Gspan) 316 ``` 317 318- 设置offset。 319 320 321 ```ts 322 let Goffset:Record<string,number> = { 'xs': 1, 'sm': 2, 'md': 3, 'lg': 4 } 323 GridCol({ offset: 2, span: 1 }){} 324 GridCol({ offset: { xs: 2, sm: 2, md: 2, lg: 2 }, span: 1 }){} 325 GridCol({ span: 1 }){}.offset(Goffset) 326 ``` 327 328- 设置order。 329 330 331 ```ts 332 let Gorder:Record<string,number> = { 'xs': 1, 'sm': 2, 'md': 3, 'lg': 4 } 333 GridCol({ order: 2, span: 1 }){} 334 GridCol({ order: { xs: 1, sm: 2, md: 3, lg: 4 }, span: 1 }){} 335 GridCol({ span: 1 }){}.order(2) 336 GridCol({ span: 1 }){}.order(Gorder) 337 ``` 338 339 340### span 341 342子组件占栅格布局的列数,决定了子组件的宽度。默认值为1。 343 344span支持number和[GridColColumnOption](../reference/apis-arkui/arkui-ts/ts-container-gridcol.md#gridcolcolumnoption)两种类型, 可按两种方式设置栅格子组件占栅格容器的列数。 345- 当span类型为number时,子组件在所有尺寸设备下占用的列数相同。 346 347 348 ```ts 349 @Entry 350 @Component 351 struct Index { 352 @State bgColors: ResourceColor[] = 353 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 354 'rgb(255,192,0)', 'rgb(170,10,33)']; 355 build() { 356 GridRow({ columns: 8 }) { 357 ForEach(this.bgColors, (color:ResourceColor, index?:number|undefined) => { 358 GridCol({ span: 2 }) { 359 Row() { 360 Text(`${index}`) 361 }.width('100%').height('50vp') 362 } 363 .backgroundColor(color) 364 }) 365 } 366 } 367 } 368 ``` 369 370  371 372- 当span类型为GridColColumnOption时,支持6种不同尺寸(xs,sm,md,lg,xl,xxl)设备中子组件所占列数设置,不同尺寸的设备下子组件支持配置不同列数。若仅部分设置sm、md的列数,未配置的xs、lg、xl、xxl设备根据[列数补全](../reference/apis-arkui/arkui-ts/ts-container-gridcol.md#gridcolcolumnoption)取默认值。 373 374 375 ```ts 376 @Entry 377 @Component 378 struct Index { 379 @State bgColors: ResourceColor[] = 380 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 381 'rgb(255,192,0)', 'rgb(170,10,33)']; 382 build() { 383 GridRow({ columns: 8 }) { 384 ForEach(this.bgColors, (color:ResourceColor, index?:number|undefined) => { 385 GridCol({ span: { xs: 1, sm: 2, md: 3, lg: 4 } }) { 386 Row() { 387 Text(`${index}`) 388 }.width('100%').height('50vp') 389 } 390 .backgroundColor(color) 391 }) 392 } 393 } 394 } 395 ``` 396 397  398 399 400### offset 401 402栅格子组件相对于前一个子组件的偏移列数,默认为0。 403 404- 当offset类型为number时,子组件偏移相同列数。 405 406 407 ```ts 408 @Entry 409 @Component 410 struct Index { 411 @State bgColors: ResourceColor[] = 412 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 413 'rgb(255,192,0)', 'rgb(170,10,33)']; 414 build() { 415 GridRow() { 416 ForEach(this.bgColors, (color:ResourceColor, index?:number|undefined) => { 417 GridCol({ offset: 2, span: 1 }) { 418 Row() { 419 Text('' + index) 420 }.width('100%').height('50vp') 421 } 422 .backgroundColor(color) 423 }) 424 } 425 } 426 } 427 ``` 428 429  430 431 栅格分成12列,每一个子组件占1列,偏移2列,每个子组件及间距共占3列,1行放4个子组件。 432 433- 当offset类型为GridColColumnOption时,支持6种不同尺寸(xs,sm,md,lg,xl,xxl)设备中子组件所占列数设置,各个尺寸下数值可不同。 434 435 436 ```ts 437 @Entry 438 @Component 439 struct Index { 440 @State bgColors: ResourceColor[] = 441 ['rgb(213,213,213)', 'rgb(150,150,150)', 'rgb(0,74,175)', 'rgb(39,135,217)', 'rgb(61,157,180)', 'rgb(23,169,141)', 442 'rgb(255,192,0)', 'rgb(170,10,33)']; 443 build() { 444 GridRow({ columns: 12 }) { 445 ForEach(this.bgColors, (color: ResourceColor, index?: number | undefined) => { 446 GridCol({ offset: { xs: 1, sm: 2, md: 3, lg: 4 }, span: 1 }) { 447 Row() { 448 Text('' + index) 449 }.width('100%').height('50vp') 450 } 451 .backgroundColor(color) 452 }) 453 } 454 .height(200) 455 .border({ color: 'rgb(39,135,217)', width: 2 }) 456 } 457 } 458 ``` 459 460  461 462 463### order 464 465栅格子组件的序号,决定子组件排列次序。当子组件不设置order或者设置相同的order, 子组件按照代码顺序展示。当子组件设置不同的order时,order较小的组件在前,较大的在后。 466 467当子组件部分设置order,部分不设置order时,未设置order的子组件依次排序靠前,设置了order的子组件按照数值从小到大排列。 468 469- 当order类型为number时,子组件在任何尺寸下排序次序一致。 470 471 472 ```ts 473 GridRow({ columns: 12 }) { 474 GridCol({ order: 4, span: 1 }) { 475 Row() { 476 Text('1') 477 }.width('100%').height('50vp') 478 }.backgroundColor('rgb(213,213,213)') 479 GridCol({ order: 3, span: 1 }) { 480 Row() { 481 Text('2') 482 }.width('100%').height('50vp') 483 }.backgroundColor('rgb(150,150,150)') 484 GridCol({ order: 2, span: 1 }) { 485 Row() { 486 Text('3') 487 }.width('100%').height('50vp') 488 }.backgroundColor('rgb(0,74,175)') 489 GridCol({ order: 1, span: 1 }) { 490 Row() { 491 Text('4') 492 }.width('100%').height('50vp') 493 }.backgroundColor('rgb(39,135,217)') 494 } 495 ``` 496 497  498 499- 当order类型为GridColColumnOption时,支持6种不同尺寸(xs,sm,md,lg,xl,xxl)设备中子组件排序次序设置。在xs设备中,子组件排列顺序为1234;sm为2341,md为3412,lg为2431。 500 501 502 ```ts 503 GridRow({ columns: 12 }) { 504 GridCol({ order: { xs:1, sm:5, md:3, lg:7}, span: 1 }) { 505 Row() { 506 Text('1') 507 }.width('100%').height('50vp') 508 }.backgroundColor(Color.Red) 509 GridCol({ order: { xs:2, sm:2, md:6, lg:1}, span:1 }) { 510 Row() { 511 Text('2') 512 }.width('100%').height('50vp') 513 }.backgroundColor(Color.Orange) 514 GridCol({ order: { xs:3, sm:3, md:1, lg:6}, span:1 }) { 515 Row() { 516 Text('3') 517 }.width('100%').height('50vp') 518 }.backgroundColor(Color.Yellow) 519 GridCol({ order: { xs:4, sm:4, md:2, lg:5}, span:1 }) { 520 Row() { 521 Text('4') 522 }.width('100%').height('50vp') 523 }.backgroundColor(Color.Green) 524 } 525 ``` 526 527  528 529 530## 栅格组件的嵌套使用 531 532栅格组件也可以嵌套使用,完成一些复杂的布局。 533 534以下示例中,栅格把整个空间分为12份。第一层GridRow嵌套GridCol,分为中间大区域以及“footer”区域。第二层GridRow嵌套GridCol,分为“left”和“right”区域。子组件空间按照上一层父组件的空间划分,粉色的区域是屏幕空间的12列,绿色和蓝色的区域是父组件GridCol的12列,依次进行空间的划分。 535 536```ts 537@Entry 538@Component 539struct GridRowExample { 540 build() { 541 GridRow({ columns: 12 }) { 542 GridCol({ span: 12 }) { 543 GridRow({ columns: 12 }) { 544 GridCol({ span: 2 }) { 545 Row() { 546 Text('left').fontSize(24) 547 } 548 .justifyContent(FlexAlign.Center) 549 .height('90%') 550 }.backgroundColor('#ff41dbaa') 551 552 GridCol({ span: 10 }) { 553 Row() { 554 Text('right').fontSize(24) 555 } 556 .justifyContent(FlexAlign.Center) 557 .height('90%') 558 }.backgroundColor('#ff4168db') 559 } 560 .backgroundColor('#19000000') 561 } 562 563 GridCol({ span: 12 }) { 564 Row() { 565 Text('footer').width('100%').textAlign(TextAlign.Center) 566 }.width('100%').height('10%').backgroundColor(Color.Pink) 567 } 568 }.width('100%').height(300) 569 } 570} 571``` 572 573 574 575 576 577综上所述,栅格组件提供了丰富的自定义能力,功能非常灵活和强大。只需要明确栅格在不同断点下的Columns、Margin、Gutter及span等参数,即可确定最终布局,无需关心具体的设备类型及设备状态(如横竖屏)等。 578