1# 弹性布局 2 3弹性布局(Flex布局)是自适应布局中使用最为灵活的布局。弹性布局提供一种更加有效的方式来对容器中的子组件进行排列、对齐和分配空白空间。弹性布局 4 5 6- 容器: [Flex组件](../reference/arkui-ts/ts-container-flex.md)作为Flex布局的容器,用于设置布局相关属性。 7- 子组件: Flex组件内的子组件自动成为布局的子组件。 8- 主轴: Flex组件布局方向的轴线,子组件默认沿着主轴排列。主轴开始的位置称为主轴起始端,结束位置称为主轴终点端。 9- 交叉轴: 垂直于主轴方向的轴线。交叉轴起始的位置称为交叉轴首部,结束位置称为交叉轴尾部。 10 11以FlexDirection.Row的Flex为例: 12 13 14 15## 容器组件属性 16 17通过Flex组件提供的Flex接口创建弹性布局。如下: 18 19`Flex(options?: { direction?: FlexDirection, wrap?: FlexWrap, justifyContent?: FlexAlign, alignItems?: ItemAlign, alignContent?: FlexAlign })` 20 21 22 23### 弹性布局方向 24参数direction决定主轴的方向,即子组件的排列方向。可选值有: 25 26 27 28- FlexDirection.Row(默认值):主轴为水平方向,子组件从起始端沿着水平方向开始排布。 29 30 ```ts 31 Flex({ direction: FlexDirection.Row }) { 32 Text('1').width('33%').height(50).backgroundColor(0xF5DEB3) 33 Text('2').width('33%').height(50).backgroundColor(0xD2B48C) 34 Text('3').width('33%').height(50).backgroundColor(0xF5DEB3) 35 } 36 .height(70) 37 .width('90%') 38 .padding(10) 39 .backgroundColor(0xAFEEEE) 40 ``` 41  42 43- FlexDirection.RowReverse:主轴为水平方向,子组件从终点端沿着FlexDirection. Row相反的方向开始排布。 44 45 ```ts 46 Flex({ direction: FlexDirection.RowReverse }) { 47 Text('1').width('33%').height(50).backgroundColor(0xF5DEB3) 48 Text('2').width('33%').height(50).backgroundColor(0xD2B48C) 49 Text('3').width('33%').height(50).backgroundColor(0xF5DEB3) 50 } 51 .height(70) 52 .width('90%') 53 .padding(10) 54 .backgroundColor(0xAFEEEE) 55 ``` 56 57  58 59- FlexDirection.Column:主轴为垂直方向,子组件从起始端沿着垂直方向开始排布。 60 61 ```ts 62 Flex({ direction: FlexDirection.Column }) { 63 Text('1').width('100%').height(50).backgroundColor(0xF5DEB3) 64 Text('2').width('100%').height(50).backgroundColor(0xD2B48C) 65 Text('3').width('100%').height(50).backgroundColor(0xF5DEB3) 66 } 67 .height(70) 68 .width('90%') 69 .padding(10) 70 .backgroundColor(0xAFEEEE) 71 ``` 72 73  74 75- FlexDirection.ColumnReverse:主轴为垂直方向,子组件从终点端沿着FlexDirection. Column相反的方向开始排布。 76 77 ```ts 78 Flex({ direction: FlexDirection.ColumnReverse }) { 79 Text('1').width('100%').height(50).backgroundColor(0xF5DEB3) 80 Text('2').width('100%').height(50).backgroundColor(0xD2B48C) 81 Text('3').width('100%').height(50).backgroundColor(0xF5DEB3) 82 } 83 .height(70) 84 .width('90%') 85 .padding(10) 86 .backgroundColor(0xAFEEEE) 87 ``` 88 89  90 91### 弹性布局换行 92 93默认情况下,子组件在Flex容器中都排在一条线(又称"轴线")上。通过wrap参数设置子组件换行方式。可选值有: 94 95- FlexWrap. NoWrap(默认值): 不换行。如果子组件的宽度总和大于父元素的宽度,则子组件会被压缩宽度。 96 97 ```ts 98 Flex({ wrap: FlexWrap.NoWrap }) { 99 Text('1').width('50%').height(50).backgroundColor(0xF5DEB3) 100 Text('2').width('50%').height(50).backgroundColor(0xD2B48C) 101 Text('3').width('50%').height(50).backgroundColor(0xF5DEB3) 102 } 103 .width('90%') 104 .padding(10) 105 .backgroundColor(0xAFEEEE) 106 ``` 107 108  109 110- FlexWrap. Wrap:换行,每一行子组件按照主轴方向排列。 111 112 ```ts 113 Flex({ wrap: FlexWrap.Wrap }) { 114 Text('1').width('50%').height(50).backgroundColor(0xF5DEB3) 115 Text('2').width('50%').height(50).backgroundColor(0xD2B48C) 116 Text('3').width('50%').height(50).backgroundColor(0xD2B48C) 117 } 118 .width('90%') 119 .padding(10) 120 .backgroundColor(0xAFEEEE) 121 ``` 122 123  124 125- FlexWrap. WrapReverse:换行,每一行子组件按照主轴反方向排列。 126 127 ```ts 128 Flex({ wrap: FlexWrap.WrapReverse}) { 129 Text('1').width('50%').height(50).backgroundColor(0xF5DEB3) 130 Text('2').width('50%').height(50).backgroundColor(0xD2B48C) 131 Text('3').width('50%').height(50).backgroundColor(0xF5DEB3) 132 } 133 .width('90%') 134 .padding(10) 135 .backgroundColor(0xAFEEEE) 136 ``` 137 138  139 140### 弹性布局对齐方式 141 142#### 主轴对齐 143 144通过justifyContent参数设置在主轴方向的对齐方式,存在下面六种情况: 145 146 147 148- FlexAlign.Start(默认值): 子组件在主轴方向起始端对齐, 第一个子组件与父元素边沿对齐,其他元素与前一个元素对齐。 149 150 ```ts 151 Flex({ justifyContent: FlexAlign.Start }) { 152 Text('1').width('20%').height(50).backgroundColor(0xF5DEB3) 153 Text('2').width('20%').height(50).backgroundColor(0xD2B48C) 154 Text('3').width('20%').height(50).backgroundColor(0xF5DEB3) 155 } 156 .width('90%') 157 .padding({ top: 10, bottom: 10 }) 158 .backgroundColor(0xAFEEEE) 159 ``` 160 161  162 163- FlexAlign.Center: 子组件在主轴方向居中对齐。 164 165 ```ts 166 Flex({ justifyContent: FlexAlign.Center }) { 167 Text('1').width('20%').height(50).backgroundColor(0xF5DEB3) 168 Text('2').width('20%').height(50).backgroundColor(0xD2B48C) 169 Text('3').width('20%').height(50).backgroundColor(0xF5DEB3) 170 } 171 .width('90%') 172 .padding({ top: 10, bottom: 10 }) 173 .backgroundColor(0xAFEEEE) 174 ``` 175 176  177 178- FlexAlign.End: 子组件在主轴方向终点端对齐, 最后一个子组件与父元素边沿对齐,其他元素与后一个元素对齐。 179 180 ```ts 181 Flex({ justifyContent: FlexAlign.End }) { 182 Text('1').width('20%').height(50).backgroundColor(0xF5DEB3) 183 Text('2').width('20%').height(50).backgroundColor(0xD2B48C) 184 Text('3').width('20%').height(50).backgroundColor(0xF5DEB3) 185 } 186 .width('90%') 187 .padding({ top: 10, bottom: 10 }) 188 .backgroundColor(0xAFEEEE) 189 ``` 190 191  192 193- FlexAlign.SpaceBetween: Flex主轴方向均匀分配弹性元素,相邻子组件之间距离相同。第一个子组件和最后一个子组件与父元素边沿对齐。 194 195 ```ts 196 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 197 Text('1').width('20%').height(50).backgroundColor(0xF5DEB3) 198 Text('2').width('20%').height(50).backgroundColor(0xD2B48C) 199 Text('3').width('20%').height(50).backgroundColor(0xF5DEB3) 200 } 201 .width('90%') 202 .padding({ top: 10, bottom: 10 }) 203 .backgroundColor(0xAFEEEE) 204 ``` 205 206  207 208- FlexAlign.SpaceAround: Flex主轴方向均匀分配弹性元素,相邻子组件之间距离相同。第一个子组件到主轴起始端的距离和最后一个子组件到主轴终点端的距离是相邻元素之间距离的一半。 209 210 ```ts 211 Flex({ justifyContent: FlexAlign.SpaceAround }) { 212 Text('1').width('20%').height(50).backgroundColor(0xF5DEB3) 213 Text('2').width('20%').height(50).backgroundColor(0xD2B48C) 214 Text('3').width('20%').height(50).backgroundColor(0xF5DEB3) 215 } 216 .width('90%') 217 .padding({ top: 10, bottom: 10 }) 218 .backgroundColor(0xAFEEEE) 219 ``` 220 221  222 223- FlexAlign.SpaceEvenly: Flex主轴方向元素等间距布局,相邻子组件之间的间距、第一个子组件与主轴起始端的间距、最后一个子组件到主轴终点端的间距均相等。 224 225 ```ts 226 Flex({ justifyContent: FlexAlign.SpaceEvenly }) { 227 Text('1').width('20%').height(50).backgroundColor(0xF5DEB3) 228 Text('2').width('20%').height(50).backgroundColor(0xD2B48C) 229 Text('3').width('20%').height(50).backgroundColor(0xF5DEB3) 230 } 231 .width('90%') 232 .padding({ top: 10, bottom: 10 }) 233 .backgroundColor(0xAFEEEE) 234 ``` 235 236  237 238#### 交叉轴对齐 239 240容器和子组件都可以设置交叉轴对齐方式,且子组件设置的对齐方式优先级较高。 241 242##### 容器组件设置交叉轴对齐 243可以通过Flex组件的alignItems参数设置子组件在交叉轴的对齐方式,可选值有: 244 245- ItemAlign.Auto: 使用Flex容器中默认配置。 246 247 ```ts 248 Flex({ alignItems: ItemAlign.Auto }) { 249 Text('1').width('33%').height(30).backgroundColor(0xF5DEB3) 250 Text('2').width('33%').height(40).backgroundColor(0xD2B48C) 251 Text('3').width('33%').height(50).backgroundColor(0xF5DEB3) 252 } 253 .size({width: '90%', height: 80}) 254 .padding(10) 255 .backgroundColor(0xAFEEEE) 256 ``` 257 258  259 260- ItemAlign.Start: 交叉轴方向首部对齐。 261 262 ```ts 263 Flex({ alignItems: ItemAlign.Start }) { 264 Text('1').width('33%').height(30).backgroundColor(0xF5DEB3) 265 Text('2').width('33%').height(40).backgroundColor(0xD2B48C) 266 Text('3').width('33%').height(50).backgroundColor(0xF5DEB3) 267 } 268 .size({width: '90%', height: 80}) 269 .padding(10) 270 .backgroundColor(0xAFEEEE) 271 ``` 272 273  274 275- ItemAlign.Center: 交叉轴方向居中对齐。 276 277 ```ts 278 Flex({ alignItems: ItemAlign.Center }) { 279 Text('1').width('33%').height(30).backgroundColor(0xF5DEB3) 280 Text('2').width('33%').height(40).backgroundColor(0xD2B48C) 281 Text('3').width('33%').height(50).backgroundColor(0xF5DEB3) 282 } 283 .size({width: '90%', height: 80}) 284 .padding(10) 285 .backgroundColor(0xAFEEEE) 286 ``` 287 288  289 290- ItemAlign.End:交叉轴方向底部对齐。 291 292 ```ts 293 Flex({ alignItems: ItemAlign.End }) { 294 Text('1').width('33%').height(30).backgroundColor(0xF5DEB3) 295 Text('2').width('33%').height(40).backgroundColor(0xD2B48C) 296 Text('3').width('33%').height(50).backgroundColor(0xF5DEB3) 297 } 298 .size({width: '90%', height: 80}) 299 .padding(10) 300 .backgroundColor(0xAFEEEE) 301 ``` 302 303  304 305- ItemAlign.Stretch:交叉轴方向拉伸填充,在未设置尺寸时,拉伸到容器尺寸。 306 307 ```ts 308 Flex({ alignItems: ItemAlign.Stretch }) { 309 Text('1').width('33%').height(30).backgroundColor(0xF5DEB3) 310 Text('2').width('33%').height(40).backgroundColor(0xD2B48C) 311 Text('3').width('33%').height(50).backgroundColor(0xF5DEB3) 312 } 313 .size({width: '90%', height: 80}) 314 .padding(10) 315 .backgroundColor(0xAFEEEE) 316 ``` 317 318  319 320- ItemAlign. Baseline:交叉轴方向文本基线对齐。 321 322 ```ts 323 Flex({ alignItems: ItemAlign.Baseline }) { 324 Text('1').width('33%').height(30).backgroundColor(0xF5DEB3) 325 Text('2').width('33%').height(40).backgroundColor(0xD2B48C) 326 Text('3').width('33%').height(50).backgroundColor(0xF5DEB3) 327 } 328 .size({width: '90%', height: 80}) 329 .padding(10) 330 .backgroundColor(0xAFEEEE) 331 ``` 332 333  334 335##### 子组件设置交叉轴对齐 336子组件的alignSelf属性也可以设置子组件在父容器交叉轴的对齐格式,且会覆盖Flex布局容器中alignItems默认配置。如下例所示: 337 338```ts 339Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) { //容器组件设置子组件居中 340 Text('alignSelf Start').width('25%').height(80) 341 .alignSelf(ItemAlign.Start) 342 .backgroundColor(0xF5DEB3) 343 Text('alignSelf Baseline') 344 .alignSelf(ItemAlign.Baseline) 345 .width('25%') 346 .height(80) 347 .backgroundColor(0xD2B48C) 348 Text('alignSelf Baseline').width('25%').height(100) 349 .backgroundColor(0xF5DEB3) 350 .alignSelf(ItemAlign.Baseline) 351 Text('no alignSelf').width('25%').height(100) 352 .backgroundColor(0xD2B48C) 353 Text('no alignSelf').width('25%').height(100) 354 .backgroundColor(0xF5DEB3) 355 356}.width('90%').height(220).backgroundColor(0xAFEEEE) 357``` 358 359 360 361上例中,Flex容器中alignItems设置交叉轴子组件的对齐方式为居中,子组件自身设置了alignSelf属性的情况,覆盖父组件的alignItem值,表现为alignSelf的定义。 362 363#### 内容对齐 364 365可以通过alignContent参数设置子组件各行在交叉轴剩余空间内的对齐方式,只在多行的flex布局中生效,可选值有: 366 367- FlexAlign.Start: 子组件各行与交叉轴起点对齐。 368 369 ```ts 370 Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.Start }) { 371 Text('1').width('30%').height(20).backgroundColor(0xF5DEB3) 372 Text('2').width('60%').height(20).backgroundColor(0xD2B48C) 373 Text('3').width('40%').height(20).backgroundColor(0xD2B48C) 374 Text('4').width('30%').height(20).backgroundColor(0xF5DEB3) 375 Text('5').width('20%').height(20).backgroundColor(0xD2B48C) 376 } 377 .width('90%') 378 .height(100) 379 .backgroundColor(0xAFEEEE) 380 ``` 381 382  383 384- FlexAlign.Center: 子组件各行在交叉轴方向居中对齐。 385 386 ```ts 387 Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.Center }) { 388 Text('1').width('30%').height(20).backgroundColor(0xF5DEB3) 389 Text('2').width('60%').height(20).backgroundColor(0xD2B48C) 390 Text('3').width('40%').height(20).backgroundColor(0xD2B48C) 391 Text('4').width('30%').height(20).backgroundColor(0xF5DEB3) 392 Text('5').width('20%').height(20).backgroundColor(0xD2B48C) 393 } 394 .width('90%') 395 .height(100) 396 .backgroundColor(0xAFEEEE) 397 ``` 398 399  400 401- FlexAlign.End: 子组件各行与交叉轴终点对齐。 402 403 ```ts 404 Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.End }) { 405 Text('1').width('30%').height(20).backgroundColor(0xF5DEB3) 406 Text('2').width('60%').height(20).backgroundColor(0xD2B48C) 407 Text('3').width('40%').height(20).backgroundColor(0xD2B48C) 408 Text('4').width('30%').height(20).backgroundColor(0xF5DEB3) 409 Text('5').width('20%').height(20).backgroundColor(0xD2B48C) 410 } 411 .width('90%') 412 .height(100) 413 .backgroundColor(0xAFEEEE) 414 ``` 415 416  417 418- FlexAlign.SpaceBetween: 子组件各行与交叉轴两端对齐,各行间垂直间距平均分布。 419 420 ```ts 421 Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceBetween }) { 422 Text('1').width('30%').height(20).backgroundColor(0xF5DEB3) 423 Text('2').width('60%').height(20).backgroundColor(0xD2B48C) 424 Text('3').width('40%').height(20).backgroundColor(0xD2B48C) 425 Text('4').width('30%').height(20).backgroundColor(0xF5DEB3) 426 Text('5').width('20%').height(20).backgroundColor(0xD2B48C) 427 } 428 .width('90%') 429 .height(100) 430 .backgroundColor(0xAFEEEE) 431 ``` 432 433  434 435- FlexAlign.SpaceAround: 子组件各行间距相等,是元素首尾行与交叉轴两端距离的两倍。 436 437 ```ts 438 Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceAround }) { 439 Text('1').width('30%').height(20).backgroundColor(0xF5DEB3) 440 Text('2').width('60%').height(20).backgroundColor(0xD2B48C) 441 Text('3').width('40%').height(20).backgroundColor(0xD2B48C) 442 Text('4').width('30%').height(20).backgroundColor(0xF5DEB3) 443 Text('5').width('20%').height(20).backgroundColor(0xD2B48C) 444 } 445 .width('90%') 446 .height(100) 447 .backgroundColor(0xAFEEEE) 448 ``` 449 450  451 452- FlexAlign.SpaceEvenly: 子组件各行间距,子组件首尾行与交叉轴两端距离都相等。 453 454 ```ts 455 Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceEvenly }) { 456 Text('1').width('30%').height(20).backgroundColor(0xF5DEB3) 457 Text('2').width('60%').height(20).backgroundColor(0xD2B48C) 458 Text('3').width('40%').height(20).backgroundColor(0xD2B48C) 459 Text('4').width('30%').height(20).backgroundColor(0xF5DEB3) 460 Text('5').width('20%').height(20).backgroundColor(0xD2B48C) 461 } 462 .width('90%') 463 .height(100) 464 .backgroundColor(0xAFEEEE) 465 ``` 466 467  468 469### 弹性布局的自适应拉伸 470 471在弹性布局父组件尺寸不够大的时候,通过子组件的下面几个属性设置其再父容器的占比,达到自适应布局能力。 472- flexBasis: 设置子组件在父容器主轴方向上的基准尺寸。如果设置了该值,则子项占用的空间为设置的值;如果没设置或者为auto,那子项的空间为width/height的值。 473 474 ```ts 475 Flex() { 476 Text('flexBasis("auto")') 477 .flexBasis('auto') // 未设置width以及flexBasis值为auto,内容自身宽松 478 .height(100) 479 .backgroundColor(0xF5DEB3) 480 Text('flexBasis("auto")'+' width("40%")') 481 .width('40%') 482 .flexBasis('auto') //设置width以及flexBasis值auto,使用width的值 483 .height(100) 484 .backgroundColor(0xD2B48C) 485 486 Text('flexBasis(100)') // 未设置width以及flexBasis值为100,宽度为100vp 487 .flexBasis(100) 488 .height(100) 489 .backgroundColor(0xF5DEB3) 490 491 Text('flexBasis(100)') 492 .flexBasis(100) 493 .width(200) // flexBasis值为100,覆盖width的设置值,宽度为100vp 494 .height(100) 495 .backgroundColor(0xD2B48C) 496 }.width('90%').height(120).padding(10).backgroundColor(0xAFEEEE) 497 ``` 498 499  500 501- flexGrow: 设置父容器的剩余空间分配给此属性所在组件的比例。用于"瓜分"父组件的剩余空间。 502 503 ```ts 504 Flex() { 505 Text('flexGrow(1)') 506 .flexGrow(2) 507 .width(100) 508 .height(100) 509 .backgroundColor(0xF5DEB3) 510 511 Text('flexGrow(2)') 512 .flexGrow(2) 513 .width(100) 514 .height(100) 515 .backgroundColor(0xD2B48C) 516 517 Text('no flexGrow') 518 .width(100) 519 .height(100) 520 .backgroundColor(0xF5DEB3) 521 }.width(400).height(120).padding(10).backgroundColor(0xAFEEEE) 522 ``` 523 524  525 526上图中,父容器宽度400vp, 三个子组件原始宽度为100vp,综合300vp,剩余空间100vp根据flexGrow值的占比分配给子组件,未设置flexGrow的子组件不参与“瓜分”。 527第一个元素以及第二个元素以2:3分配剩下的100vp。第一个元素为100vp+100vp*2/5=140vp,第二个元素为100vp+100vp*3/5=160vp。 528 529- flexShrink: 当父容器空间不足时,子组件的压缩比例。 530 531 ```ts 532 Flex({ direction: FlexDirection.Row }) { 533 Text('flexShrink(3)') 534 .flexShrink(3) 535 .width(200) 536 .height(100) 537 .backgroundColor(0xF5DEB3) 538 539 Text('no flexShrink') 540 .width(200) 541 .height(100) 542 .backgroundColor(0xD2B48C) 543 544 Text('flexShrink(2)') 545 .flexShrink(2) 546 .width(200) 547 .height(100) 548 .backgroundColor(0xF5DEB3) 549 }.width(400).height(120).padding(10).backgroundColor(0xAFEEEE) 550 ``` 551 552  553 554## 场景示例 555 556使用弹性布局,可以实现子组件沿水平方向排列,两端对齐,子组件间距平分,竖直方向上子组件居中的效果。示例如下: 557 558```ts 559@Entry 560@Component 561struct FlexExample { 562 build() { 563 Column() { 564 Column({ space: 5 }) { 565 Flex({ direction: FlexDirection.Row, wrap: FlexWrap.NoWrap, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { 566 Text('1').width('30%').height(50).backgroundColor(0xF5DEB3) 567 Text('2').width('30%').height(50).backgroundColor(0xD2B48C) 568 Text('3').width('30%').height(50).backgroundColor(0xF5DEB3) 569 } 570 .height(70) 571 .width('90%') 572 .backgroundColor(0xAFEEEE) 573 }.width('100%').margin({ top: 5 }) 574 }.width('100%') 575 } 576} 577``` 578 579 580 581## 相关实例 582 583针对弹性布局开发,有以下相关实例可供参考: 584 585- [弹性布局(ArkTS)(API8)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/FlowLayoutEts) 586 587