1# 线性布局 (Row/Column) 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @camlostshi--> 5<!--Designer: @lanshouren--> 6<!--Tester: @liuli0427--> 7<!--Adviser: @HelloCrease--> 8 9 10## 概述 11 12线性布局(LinearLayout)是开发中最常用的布局,通过线性容器[Row](../reference/apis-arkui/arkui-ts/ts-container-row.md)和[Column](../reference/apis-arkui/arkui-ts/ts-container-column.md)构建。线性布局是其他布局的基础,其子元素在线性方向上(水平方向和垂直方向)依次排列。线性布局的排列方向由所选容器组件决定,Row容器内子元素按照水平方向排列,Column容器内子元素按照垂直方向排列。根据不同的排列方向,开发者可选择使用Row或Column容器创建线性布局。 13 14> **说明:** 15> 16> 在复杂界面中使用多组件嵌套时,若布局组件的嵌套层数过深或嵌套的组件数量过多,将会产生额外开销。建议通过移除冗余节点、利用布局边界减少布局计算、合理采用渲染控制语法及布局组件方法来优化性能。最佳实践请参考[合理使用布局](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-improve-layout-performance)。 17 18 19 **图1** Column容器内子元素排列示意图 20 21 22 23 24 **图2** Row容器内子元素排列示意图 25 26 27 28 29## 基本概念 30 31- 布局容器:具有布局能力的容器组件,可以承载其他元素作为其子元素,布局容器会对其子元素进行尺寸计算和布局排列。 32 33- 布局子元素:布局容器内部的元素。 34 35- 主轴:线性布局容器在布局方向上的轴线,子元素默认沿主轴排列。Row容器主轴为水平方向,Column容器主轴为垂直方向(图示可参考弹性布局[基本概念](./arkts-layout-development-flex-layout.md#基本概念)中的主轴)。 36 37- 交叉轴:垂直于主轴方向的轴线。Row容器交叉轴为垂直方向,Column容器交叉轴为水平方向(图示可参考弹性布局[基本概念](./arkts-layout-development-flex-layout.md#基本概念)中的交叉轴)。 38 39- 间距:布局子元素的间距。 40 41 42## 布局子元素在排列方向上的间距 43 44在布局容器内,可以通过space属性设置排列方向上子元素的间距,使各子元素在排列方向上有等间距效果。 45 46 47### Column容器内排列方向上的间距 48 49 **图3** Column容器内排列方向的间距图 50 51 52 53```ts 54Column({ space: 20 }) { 55 Text('space: 20').fontSize(15).fontColor(Color.Gray).width('90%') 56 Row().width('90%').height(50).backgroundColor(0xF5DEB3) 57 Row().width('90%').height(50).backgroundColor(0xD2B48C) 58 Row().width('90%').height(50).backgroundColor(0xF5DEB3) 59}.width('100%') 60``` 61 62 63 64 65 66### Row容器内排列方向上的间距 67 68 **图4** Row容器内排列方向的间距图 69 70 71 72 73```ts 74Row({ space: 35 }) { 75 Text('space: 35').fontSize(15).fontColor(Color.Gray) 76 Row().width('10%').height(150).backgroundColor(0xF5DEB3) 77 Row().width('10%').height(150).backgroundColor(0xD2B48C) 78 Row().width('10%').height(150).backgroundColor(0xF5DEB3) 79}.width('90%') 80``` 81 82 83 84## 布局子元素在主轴上的排列方式 85 86在布局容器内,可以通过justifyContent属性设置子元素在容器主轴上的排列方式。可以从主轴起始位置开始排布,也可以从主轴结束位置开始排布,或者均匀分割主轴的空间。 87 88 89### Column容器内子元素在垂直方向上的排列 90 91 **图5** Column容器内子元素在垂直方向上的排列图 92 93 94 95- justifyContent(FlexAlign.Start):元素在垂直方向首端对齐,第一个元素与行首对齐,同时后续的元素与前一个对齐。 96 97 ```ts 98 Column({}) { 99 Column() { 100 }.width('80%').height(50).backgroundColor(0xF5DEB3) 101 102 Column() { 103 }.width('80%').height(50).backgroundColor(0xD2B48C) 104 105 Column() { 106 }.width('80%').height(50).backgroundColor(0xF5DEB3) 107 }.width('100%').height(300).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.Start) 108 ``` 109 110  111 112- justifyContent(FlexAlign.Center):元素在垂直方向中心对齐,第一个元素与行首的距离与最后一个元素与行尾距离相同。 113 114 ```ts 115 Column({}) { 116 Column() { 117 }.width('80%').height(50).backgroundColor(0xF5DEB3) 118 119 Column() { 120 }.width('80%').height(50).backgroundColor(0xD2B48C) 121 122 Column() { 123 }.width('80%').height(50).backgroundColor(0xF5DEB3) 124 }.width('100%').height(300).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.Center) 125 ``` 126 127  128 129- justifyContent(FlexAlign.End):元素在垂直方向尾部对齐,最后一个元素与行尾对齐,其他元素与后一个对齐。 130 131 ```ts 132 Column({}) { 133 Column() { 134 }.width('80%').height(50).backgroundColor(0xF5DEB3) 135 136 Column() { 137 }.width('80%').height(50).backgroundColor(0xD2B48C) 138 139 Column() { 140 }.width('80%').height(50).backgroundColor(0xF5DEB3) 141 }.width('100%').height(300).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.End) 142 ``` 143 144  145 146- justifyContent(FlexAlign.SpaceBetween):垂直方向均匀分配元素,相邻元素之间距离相同。第一个元素与行首对齐,最后一个元素与行尾对齐。 147 148 ```ts 149 Column({}) { 150 Column() { 151 }.width('80%').height(50).backgroundColor(0xF5DEB3) 152 153 Column() { 154 }.width('80%').height(50).backgroundColor(0xD2B48C) 155 156 Column() { 157 }.width('80%').height(50).backgroundColor(0xF5DEB3) 158 }.width('100%').height(300).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.SpaceBetween) 159 ``` 160 161  162 163- justifyContent(FlexAlign.SpaceAround):垂直方向均匀分配元素,相邻元素之间距离相同。第一个元素到行首的距离和最后一个元素到行尾的距离是相邻元素之间距离的一半。 164 165 ```ts 166 Column({}) { 167 Column() { 168 }.width('80%').height(50).backgroundColor(0xF5DEB3) 169 170 Column() { 171 }.width('80%').height(50).backgroundColor(0xD2B48C) 172 173 Column() { 174 }.width('80%').height(50).backgroundColor(0xF5DEB3) 175 }.width('100%').height(300).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.SpaceAround) 176 ``` 177 178  179 180- justifyContent(FlexAlign.SpaceEvenly):垂直方向均匀分配元素,相邻元素之间的距离、第一个元素与行首的间距、最后一个元素到行尾的间距都完全一样。 181 182 ```ts 183 Column({}) { 184 Column() { 185 }.width('80%').height(50).backgroundColor(0xF5DEB3) 186 187 Column() { 188 }.width('80%').height(50).backgroundColor(0xD2B48C) 189 190 Column() { 191 }.width('80%').height(50).backgroundColor(0xF5DEB3) 192 }.width('100%').height(300).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.SpaceEvenly) 193 ``` 194 195  196 197 198### Row容器内子元素在水平方向上的排列 199 200 **图6** Row容器内子元素在水平方向上的排列图 201 202 203 204- justifyContent(FlexAlign.Start):元素在水平方向首端对齐,第一个元素与行首对齐,同时后续的元素与前一个对齐。 205 206 ```ts 207 Row({}) { 208 Column() { 209 }.width('20%').height(30).backgroundColor(0xF5DEB3) 210 211 Column() { 212 }.width('20%').height(30).backgroundColor(0xD2B48C) 213 214 Column() { 215 }.width('20%').height(30).backgroundColor(0xF5DEB3) 216 }.width('100%').height(200).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.Start) 217 ``` 218 219  220 221- justifyContent(FlexAlign.Center):元素在水平方向中心对齐,第一个元素与行首的距离与最后一个元素与行尾距离相同。 222 223 ```ts 224 Row({}) { 225 Column() { 226 }.width('20%').height(30).backgroundColor(0xF5DEB3) 227 228 Column() { 229 }.width('20%').height(30).backgroundColor(0xD2B48C) 230 231 Column() { 232 }.width('20%').height(30).backgroundColor(0xF5DEB3) 233 }.width('100%').height(200).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.Center) 234 ``` 235 236  237 238- justifyContent(FlexAlign.End):元素在水平方向尾部对齐,最后一个元素与行尾对齐,其他元素与后一个对齐。 239 240 ```ts 241 Row({}) { 242 Column() { 243 }.width('20%').height(30).backgroundColor(0xF5DEB3) 244 245 Column() { 246 }.width('20%').height(30).backgroundColor(0xD2B48C) 247 248 Column() { 249 }.width('20%').height(30).backgroundColor(0xF5DEB3) 250 }.width('100%').height(200).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.End) 251 ``` 252 253  254 255- justifyContent(FlexAlign.SpaceBetween):水平方向均匀分配元素,相邻元素之间距离相同。第一个元素与行首对齐,最后一个元素与行尾对齐。 256 257 ```ts 258 Row({}) { 259 Column() { 260 }.width('20%').height(30).backgroundColor(0xF5DEB3) 261 262 Column() { 263 }.width('20%').height(30).backgroundColor(0xD2B48C) 264 265 Column() { 266 }.width('20%').height(30).backgroundColor(0xF5DEB3) 267 }.width('100%').height(200).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.SpaceBetween) 268 ``` 269 270  271 272- justifyContent(FlexAlign.SpaceAround):水平方向均匀分配元素,相邻元素之间距离相同。第一个元素到行首的距离和最后一个元素到行尾的距离是相邻元素之间距离的一半。 273 274 ```ts 275 Row({}) { 276 Column() { 277 }.width('20%').height(30).backgroundColor(0xF5DEB3) 278 279 Column() { 280 }.width('20%').height(30).backgroundColor(0xD2B48C) 281 282 Column() { 283 }.width('20%').height(30).backgroundColor(0xF5DEB3) 284 }.width('100%').height(200).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.SpaceAround) 285 ``` 286 287  288 289- justifyContent(FlexAlign.SpaceEvenly):水平方向均匀分配元素,相邻元素之间的距离、第一个元素与行首的间距、最后一个元素到行尾的间距都完全一样。 290 291 ```ts 292 Row({}) { 293 Column() { 294 }.width('20%').height(30).backgroundColor(0xF5DEB3) 295 296 Column() { 297 }.width('20%').height(30).backgroundColor(0xD2B48C) 298 299 Column() { 300 }.width('20%').height(30).backgroundColor(0xF5DEB3) 301 }.width('100%').height(200).backgroundColor('rgb(242,242,242)').justifyContent(FlexAlign.SpaceEvenly) 302 ``` 303 304  305 306## 布局子元素在交叉轴上的对齐方式 307 308在布局容器内,可以通过alignItems属性设置子元素在交叉轴(排列方向的垂直方向)上的对齐方式,且在各类尺寸屏幕中表现一致。其中,交叉轴为垂直方向时,取值为[VerticalAlign](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#verticalalign)类型,水平方向取值为[HorizontalAlign](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#horizontalalign)类型。 309 310alignSelf属性用于控制单个子元素在容器交叉轴上的对齐方式,其优先级高于alignItems属性,如果设置了alignSelf属性,则在单个子元素上会覆盖alignItems属性。 311 312 313### Column容器内子元素在水平方向上的排列 314 315 **图7** Column容器内子元素在水平方向上的排列图 316 317 318 319- HorizontalAlign.Start:子元素在水平方向左对齐。 320 321 ```ts 322 Column({}) { 323 Column() { 324 }.width('80%').height(50).backgroundColor(0xF5DEB3) 325 326 Column() { 327 }.width('80%').height(50).backgroundColor(0xD2B48C) 328 329 Column() { 330 }.width('80%').height(50).backgroundColor(0xF5DEB3) 331 }.width('100%').alignItems(HorizontalAlign.Start).backgroundColor('rgb(242,242,242)') 332 ``` 333 334  335 336- HorizontalAlign.Center:子元素在水平方向居中对齐。 337 338 ```ts 339 Column({}) { 340 Column() { 341 }.width('80%').height(50).backgroundColor(0xF5DEB3) 342 343 Column() { 344 }.width('80%').height(50).backgroundColor(0xD2B48C) 345 346 Column() { 347 }.width('80%').height(50).backgroundColor(0xF5DEB3) 348 }.width('100%').alignItems(HorizontalAlign.Center).backgroundColor('rgb(242,242,242)') 349 ``` 350 351  352 353- HorizontalAlign.End:子元素在水平方向右对齐。 354 355 ```ts 356 Column({}) { 357 Column() { 358 }.width('80%').height(50).backgroundColor(0xF5DEB3) 359 360 Column() { 361 }.width('80%').height(50).backgroundColor(0xD2B48C) 362 363 Column() { 364 }.width('80%').height(50).backgroundColor(0xF5DEB3) 365 }.width('100%').alignItems(HorizontalAlign.End).backgroundColor('rgb(242,242,242)') 366 ``` 367 368  369 370 371### Row容器内子元素在垂直方向上的排列 372 373 **图8** Row容器内子元素在垂直方向上的排列图 374 375 376 377- VerticalAlign.Top:子元素在垂直方向顶部对齐。 378 379 ```ts 380 Row({}) { 381 Column() { 382 }.width('20%').height(30).backgroundColor(0xF5DEB3) 383 384 Column() { 385 }.width('20%').height(30).backgroundColor(0xD2B48C) 386 387 Column() { 388 }.width('20%').height(30).backgroundColor(0xF5DEB3) 389 }.width('100%').height(200).alignItems(VerticalAlign.Top).backgroundColor('rgb(242,242,242)') 390 ``` 391 392  393 394- VerticalAlign.Center:子元素在垂直方向居中对齐。 395 396 ```ts 397 Row({}) { 398 Column() { 399 }.width('20%').height(30).backgroundColor(0xF5DEB3) 400 401 Column() { 402 }.width('20%').height(30).backgroundColor(0xD2B48C) 403 404 Column() { 405 }.width('20%').height(30).backgroundColor(0xF5DEB3) 406 }.width('100%').height(200).alignItems(VerticalAlign.Center).backgroundColor('rgb(242,242,242)') 407 ``` 408 409  410 411- VerticalAlign.Bottom:子元素在垂直方向底部对齐。 412 413 ```ts 414 Row({}) { 415 Column() { 416 }.width('20%').height(30).backgroundColor(0xF5DEB3) 417 418 Column() { 419 }.width('20%').height(30).backgroundColor(0xD2B48C) 420 421 Column() { 422 }.width('20%').height(30).backgroundColor(0xF5DEB3) 423 }.width('100%').height(200).alignItems(VerticalAlign.Bottom).backgroundColor('rgb(242,242,242)') 424 ``` 425 426  427 428## 自适应拉伸 429 430在线性布局下,常用空白填充组件[Blank](../reference/apis-arkui/arkui-ts/ts-basic-components-blank.md),在容器主轴方向自动填充空白空间,达到自适应拉伸效果。Row和Column作为容器,只需要添加宽高为百分比,当屏幕宽高发生变化时,会产生自适应效果。 431 432 433```ts 434@Entry 435@Component 436struct BlankExample { 437 build() { 438 Column() { 439 Row() { 440 Text('Bluetooth').fontSize(18) 441 Blank() 442 Toggle({ type: ToggleType.Switch, isOn: true }) 443 }.backgroundColor(0xFFFFFF).borderRadius(15).padding({ left: 12 }).width('100%') 444 }.backgroundColor(0xEFEFEF).padding(20).width('100%') 445 } 446} 447``` 448 449 **图9** 竖屏(自适应屏幕窄边) 450 451 452 453 **图10** 横屏(自适应屏幕宽边) 454 455 456 457 458## 自适应缩放 459 460自适应缩放是指子元素随容器尺寸的变化而按照预设的比例自动调整尺寸,适应各种不同大小的设备。在线性布局中,可以使用以下两种方法实现自适应缩放。 461 462 463- 父容器尺寸确定时,使用layoutWeight属性设置子元素和兄弟元素在主轴上的权重,忽略元素本身尺寸设置,使它们在任意尺寸的设备下自适应占满剩余空间。 464 465 ```ts 466 @Entry 467 @Component 468 struct layoutWeightExample { 469 build() { 470 Column() { 471 Text('1:2:3').width('100%') 472 Row() { 473 Column() { 474 Text('layoutWeight(1)') 475 .textAlign(TextAlign.Center) 476 }.layoutWeight(1).backgroundColor(0xF5DEB3).height('100%') 477 478 Column() { 479 Text('layoutWeight(2)') 480 .textAlign(TextAlign.Center) 481 }.layoutWeight(2).backgroundColor(0xD2B48C).height('100%') 482 483 Column() { 484 Text('layoutWeight(3)') 485 .textAlign(TextAlign.Center) 486 }.layoutWeight(3).backgroundColor(0xF5DEB3).height('100%') 487 488 }.backgroundColor(0xffd306).height('30%') 489 490 Text('2:5:3').width('100%') 491 Row() { 492 Column() { 493 Text('layoutWeight(2)') 494 .textAlign(TextAlign.Center) 495 }.layoutWeight(2).backgroundColor(0xF5DEB3).height('100%') 496 497 Column() { 498 Text('layoutWeight(5)') 499 .textAlign(TextAlign.Center) 500 }.layoutWeight(5).backgroundColor(0xD2B48C).height('100%') 501 502 Column() { 503 Text('layoutWeight(3)') 504 .textAlign(TextAlign.Center) 505 }.layoutWeight(3).backgroundColor(0xF5DEB3).height('100%') 506 }.backgroundColor(0xffd306).height('30%') 507 } 508 } 509 } 510 ``` 511 512 **图11** 横屏 513 514  515 516 **图12** 竖屏 517 518  519 520- 父容器尺寸确定时,使用百分比设置子元素和兄弟元素的宽度,使他们在任意尺寸的设备下保持固定的自适应占比。 521 522 ```ts 523 @Entry 524 @Component 525 struct WidthExample { 526 build() { 527 Column() { 528 Row() { 529 Column() { 530 Text('left width 20%') 531 .textAlign(TextAlign.Center) 532 }.width('20%').backgroundColor(0xF5DEB3).height('100%') 533 534 Column() { 535 Text('center width 50%') 536 .textAlign(TextAlign.Center) 537 }.width('50%').backgroundColor(0xD2B48C).height('100%') 538 539 Column() { 540 Text('right width 30%') 541 .textAlign(TextAlign.Center) 542 }.width('30%').backgroundColor(0xF5DEB3).height('100%') 543 }.backgroundColor(0xffd306).height('30%') 544 } 545 } 546 } 547 ``` 548 549 **图13** 横屏 550 551  552 553 **图14** 竖屏 554 555  556 557 558## 自适应延伸 559 560自适应延伸是指在不同尺寸设备下,当页面的内容超出屏幕大小而无法完全显示时,可以通过滚动条进行拖动展示。对于线性布局,这种方法适用于容器中内容无法一屏展示的场景。通常有以下两种实现方式。 561 562- [在List中添加滚动条](arkts-layout-development-create-list.md#添加滚动条):当List子项过多一屏放不下时,可以将每一项子元素放置在不同的组件中,通过滚动条进行拖动展示。可以通过scrollBar属性设置滚动条的常驻状态,edgeEffect属性设置拖动到内容最末端的回弹效果。 563 564- 使用Scroll组件:在线性布局中,开发者可以进行垂直方向或者水平方向的布局。当一屏无法完全显示时,可以在Column或Row组件的外层包裹一个可滚动的容器组件Scroll来实现可滑动的线性布局。 565 垂直方向布局中使用Scroll组件: 566 567 ```ts 568 @Entry 569 @Component 570 struct ScrollExample { 571 scroller: Scroller = new Scroller(); 572 private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 573 574 build() { 575 Scroll(this.scroller) { 576 Column() { 577 ForEach(this.arr, (item?:number|undefined) => { 578 if(item){ 579 Text(item.toString()) 580 .width('90%') 581 .height(150) 582 .backgroundColor(0xFFFFFF) 583 .borderRadius(15) 584 .fontSize(16) 585 .textAlign(TextAlign.Center) 586 .margin({ top: 10 }) 587 } 588 }, (item:number) => item.toString()) 589 }.width('100%') 590 } 591 .backgroundColor(0xDCDCDC) 592 .scrollable(ScrollDirection.Vertical) // 滚动方向为垂直方向 593 .scrollBar(BarState.On) // 滚动条常驻显示 594 .scrollBarColor(Color.Gray) // 滚动条颜色 595 .scrollBarWidth(10) // 滚动条宽度 596 .edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹 597 } 598 } 599 ``` 600 601  602 603 水平方向布局中使用Scroll组件: 604 605 606 ```ts 607 @Entry 608 @Component 609 struct ScrollExample { 610 scroller: Scroller = new Scroller(); 611 private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 612 613 build() { 614 Scroll(this.scroller) { 615 Row() { 616 ForEach(this.arr, (item?:number|undefined) => { 617 if(item){ 618 Text(item.toString()) 619 .height('90%') 620 .width(150) 621 .backgroundColor(0xFFFFFF) 622 .borderRadius(15) 623 .fontSize(16) 624 .textAlign(TextAlign.Center) 625 .margin({ left: 10 }) 626 } 627 }) 628 }.height('100%') 629 } 630 .backgroundColor(0xDCDCDC) 631 .scrollable(ScrollDirection.Horizontal) // 滚动方向为水平方向 632 .scrollBar(BarState.On) // 滚动条常驻显示 633 .scrollBarColor(Color.Gray) // 滚动条颜色 634 .scrollBarWidth(10) // 滚动条宽度 635 .edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹 636 } 637 } 638 ``` 639 640  641