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