1# Improving Layout Performance 2 3## Background 4 5Layouts in the user interface (UI) are a key part of interactions between users and your application. A UI with diverse, well-designed layouts is more enjoyable to interact with. Yet, if poorly implemented, layouts can also lead to performance bottlenecks. Poor layouts may still appear, on the surface, attractive, but the huge overhead underneath – resulting from transition layout calculation and unnecessary nesting, can undermine performance. This document provides tips for optimizing the layout structure, with primary focus on most common layouts and their use cases. 6 7## Common Layouts 8 9The layout defines how components are laid out in the UI. ArkUI offers a diverse array of layouts. Besides the basic layouts – [linear](../ui/arkts-layout-development-linear.md) ([Row](../reference/apis-arkui/arkui-ts/ts-container-row.md)/[Column](../reference/apis-arkui/arkui-ts/ts-container-column.md)), [stack](../ui/arkts-layout-development-stack-layout.md) ([Stack](../reference/apis-arkui/arkui-ts/ts-container-stack.md)), [flex](../ui/arkts-layout-development-flex-layout.md) ([Flex](../reference/apis-arkui/arkui-ts/ts-container-flex.md)), [relative](../ui/arkts-layout-development-relative-layout.md) ([RelativeContainer](../reference/apis-arkui/arkui-ts/ts-container-relativecontainer.md)), and [responsive grid](../ui/arkts-layout-development-grid-layout.md) ([GridCol](../reference/apis-arkui/arkui-ts/ts-container-gridcol.md)), you also have access to the advanced [list](../ui/arkts-layout-development-create-list.md) ([List](../reference/apis-arkui/arkui-ts/ts-container-list.md)), [grid](../ui/arkts-layout-development-create-grid.md) ([Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md)/[\<GridItem>](../reference/apis-arkui/arkui-ts/ts-container-griditem.md)), and [swiper](../ui/arkts-layout-development-create-looping.md) ([Swiper](../reference/apis-arkui/arkui-ts/ts-container-swiper.md)) layouts. 10 11## Optimizing Layout Structure 12 13### Reducing Nesting 14 15Compared with a flat layout, a deep layout with too much nesting means more time spent on node creation and layout. For optimized layout hierarchies, avoid redundant nesting or flatten the layout. 16 17**Avoiding Redundant Nesting** 18 19Redundant nesting results in unnecessary component nodes and deepens the hierarchy of the component tree. For example, if internal and external containers have the same layout direction, the external container can be used in place of the internal one to deliver the same layout effect. In this case, the redundant container can be removed to reduce nesting. 20 21Nonexample: 22 23The following uses the **Grid** component to implement a grid, outside of which three layers of stack containers with different attributes are deployed. 24 25```ts 26@Entry 27@Component 28struct AspectRatioExample12 { 29 @State children: Number[] = Array.from(Array<number>(900), (v, k) => k); 30 31 build() { 32 Scroll() { 33 Grid() { 34 ForEach(this.children, (item: Number[]) => { 35 GridItem() { 36 Stack() { 37 Stack() { 38 Stack() { 39 Text(item.toString()) 40 }.size({ width: "100%"}) 41 }.backgroundColor(Color.Yellow) 42 }.backgroundColor(Color.Pink) 43 } 44 }, (item: string) => item) 45 } 46 .columnsTemplate('1fr 1fr 1fr 1fr') 47 .columnsGap(0) 48 .rowsGap(0) 49 .size({ width: "100%", height: "100%" }) 50 } 51 } 52} 53 54``` 55 56A review of the component tree structure reveals that, the desirable UI effect can be equally achieved by the attribute settings of the grid items. In this sense, the three-layer stack containers are redundant and can be removed. 57 58 59``` 60└─┬Scroll 61 └─┬Grid 62 ├─┬GridItem 63 │ └─┬Stack 64 │ └─┬Stack 65 │ └─┬Stack 66 │ └──Text 67 ├──GridItem 68 ├──GridItem 69``` 70 71Example: 72 73In the following code, by removing redundant nesting of stack containers, the number of child components in each grid item is three less than that in the previous example. 74 75```ts 76@Entry 77@Component 78struct AspectRatioExample11 { 79 @State children: Number[] = Array.from(Array<number>(900), (v, k) => k); 80 81 build() { 82 Scroll() { 83 Grid() { 84 ForEach(this.children, (item: Number[]) => { 85 GridItem() { 86 Text(item.toString()) 87 }.backgroundColor(Color.Yellow) 88 }, (item: string) => item) 89 } 90 .columnsTemplate('1fr 1fr 1fr 1fr') 91 .columnsGap(0) 92 .rowsGap(0) 93 .size({ width: "100%", height: "100%" }) 94 } 95 } 96} 97``` 98 99The component tree structure is as follows: 100 101``` 102└─┬Scroll 103 └─┬Grid 104 ├─┬GridItem 105 │ └──Text 106 ├──GridItem 107 ├──GridItem 108``` 109 110**Flattening Layout** 111 112In implementing an adaptive layout, use of the **Flex** component can cause multi-level nesting. In this scenario, you are advised to use **RelativeContainer** to flatten the layout to effectively reduce nesting and shorten the component creation time. 113 114The figure below shows an adaptive UI. 115 116 117 118Nonexample: 119 120The following code uses a linear layout to implement the UI shown above: 121 122```ts 123@Entry 124@Component 125struct MyComponent { 126 build() { 127 Row() { 128 Column() { 129 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 130 Text ('J') 131 // For details about the attribute parameters, see the example. 132 } 133 .width("40vp") 134 .height("40vp") 135 }.height("100%").justifyContent(FlexAlign.Center) 136 //body 137 Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start }) { 138 Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center }) { 139 Flex({ direction: FlexDirection.Row, 140 justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { 141 //Phone number or first name 142 Text('John') 143 // For details about the attribute parameters, see the example. 144 145 //Date Time 146 Text ('2 min ago') 147 // For details about the attribute parameters, see the example. 148 } 149 .width("100%").height(22) 150 151 Row() { 152 Text() { 153 //Content Abbreviations for Latest News 154 Span('Hello World'.replace(new RegExp("/[\r\n]/g"), " ")) 155 .fontSize("14fp") 156 .fontColor('# 66182431') 157 } 158 .maxLines(1) 159 .textOverflow({ overflow: TextOverflow.Ellipsis }) 160 } 161 .alignSelf(ItemAlign.Start) 162 .alignItems(VerticalAlign.Top) 163 .width("100%") 164 .height(19) 165 .margin({ top: "2vp" }) 166 }.width("100%") 167 .height("100%") 168 } 169 .layoutWeight(1) 170 .height("100%") 171 .padding({ left: "12vp" }) 172 } 173 .alignItems(VerticalAlign.Top) 174 .width("100%") 175 .height("100%") 176 } 177} 178``` 179 180The component tree structure is as follows: 181 182``` 183└─┬Row 184 ├──┬Column 185 │ └─┬Flex 186 │ └──Text 187 └─┬Flex 188 └─┬Flex 189 │ └─┬Flex 190 │ ├──Text 191 │ └──Text 192 └─┬Row 193 └──Text 194``` 195 196To place four elements in proper positions, 11 components are used with a tree depth of 5, which is actually unreasonable. 197 198The figure below illustrates the layout of the elements. 199 200 201 202Example: 203 204The figure above shows a clear relative layout relationship. In light of this, a **RelativeContainer** can be adopted to improve performance. Below is the specific code implementation. 205 206```ts 207@Entry 208@Component 209struct MyComponent { 210 build() { 211 Row() { 212 RelativeContainer() { 213 Text ('J') 214 .fontSize('20.0vp') 215 .fontWeight(FontWeight.Bold) 216 .fontColor(Color.White) 217 .height('40vp') 218 .width('40vp') 219 .textAlign(TextAlign.Center) 220 .clip(new Circle({ width: '40vp', height: '40vp' })) 221 .backgroundColor(Color.Green) 222 .alignRules({ 223 center: { anchor: "__container__", align: VerticalAlign.Center }, 224 left: { anchor: "__container__", align: HorizontalAlign.Start } 225 }) 226 .id('head') 227 Text('John') 228 .fontSize('16.0fp') 229 .textOverflow({ overflow: TextOverflow.Ellipsis }) 230 .fontColor('# ff182431') 231 .maxLines(1) 232 .fontWeight(FontWeight.Medium) 233 .padding({ left: '12vp' }) 234 .height(22) 235 .alignRules({ 236 top: { anchor: 'head', align: VerticalAlign.Top }, 237 left: { anchor: 'head', align: HorizontalAlign.End } 238 }) 239 .id('name') 240 Text ('2 min ago') 241 .fontColor('# 66182431') 242 .fontSize('12fp') 243 .maxLines(1) 244 .height(22) 245 .alignRules({ 246 top: { anchor: 'head', align: VerticalAlign.Top }, 247 right: { anchor: '__container__', align: HorizontalAlign.End } 248 }) 249 .id("time") 250 Text() { 251 //Content Abbreviations for Latest News 252 Span('Hello World'.replace(new RegExp("/[\r\n]/g"), " ")) 253 .fontSize('14fp') 254 .fontColor('# 66182431') 255 } 256 .maxLines(1) 257 .textOverflow({ overflow: TextOverflow.Ellipsis }) 258 .width('100%') 259 .height(19) 260 .margin({ top: '2vp' }) 261 .padding({ left: '12vp' }) 262 .alignRules({ 263 top: { anchor: 'name', align: VerticalAlign.Bottom }, 264 left: { anchor: 'head', align: HorizontalAlign.End } 265 }) 266 .id('content') 267 } 268 .width('100%').height('100%') 269 .border({ width: 1, color: "# 6699FF" }) 270 } 271 .height('100%') 272 } 273} 274``` 275 276The new layout, which delivers the same effect as the previous deep layout, has three less component layers and six less components. 277 278``` 279└─┬RelativeContainer 280 ├──Text 281 ├──Text 282 ├──Text 283 └──Text 284``` 285 286As shown in the preceding examples, the flattened layout presents clearer logic design, avoiding components that are not involved in drawing for better performance and less memory usage. By flattening the layout, you collapse the hierarchy levels of a deep UI tree and re-arrange content into under one top node. The figure below illustrates how layout flattening helps remove the redundant layout nodes. 287 288 289 290Tools available for flattening the layout include, among others, the [RelativeContainer](../reference/apis-arkui/arkui-ts/ts-container-relativecontainer.md) component, [Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md) component, and [absolute location](../reference/apis-arkui/arkui-ts/ts-universal-attributes-location.md). 291 292### Using High-Performance Layout Components 293 294**Replacing Flex Containers with Columns/Rows** 295 296If you are using the flex container to merely implement a horizontal or vertical layout, the **Row** and **Column** components are better choices in terms of rendering performance benefits. For details about the impact of the flex container on performance, see [Flex Layout Performance Improvement](flex-development-performance-boost.md). 297 298For details about how to replace the flex container components with **Row** and **Column** components to avoid secondary rendering, see [More Performance Improvement Methods](arkts-performance-improvement-recommendation.md). 299 300**Reducing Use of if/else** 301 302In the build function of ArkUI, **if/else** is also regarded as a component and a node in the component tree. In scenarios where the basic layout remains unchanged, reduce the use of if/else if modifying attribute settings can achieve the same purpose – present different GUI content under different conditions. The use of **if/else** not only adds a layer of nodes, but might also result in re-layout and re-render. 303 304Nonexample: 305 306In the following code, the value of **isVisible** is used to control the visibility of the **Image** component. As a result, the **Image** component is continuously created and destroyed during the selection switchover. 307 308```ts 309@Entry 310@Component 311struct TopicItem { 312 @State isVisible : Boolean = true; 313 314 build() { 315 Stack() { 316 Column(){ 317 if (this.isVisible) { 318 Image($r('app.media.icon')).width('25%').height('12.5%') 319 Image($r('app.media.icon')).width('25%').height('12.5%') 320 Image($r('app.media.icon')).width('25%').height('12.5%') 321 Image($r('app.media.icon')).width('25%').height('12.5%') 322 } 323 } 324 Column() { 325 Row().width(300).height(200).backgroundColor(Color.Pink) 326 } 327 } 328 } 329} 330``` 331 332Below are the component trees at different values of **isVisible**. 333 334``` 335Component tree when **isVisible** is **true**: 336└─┬Stack 337 ├─┬Column 338 │ ├──Image 339 │ ├──Image 340 │ ├──Image 341 │ └──Image 342 └─┬Column 343 └──Row 344 345Component tree when **isVisible** is **false**: 346└─┬Stack 347 ├──Column 348 └─┬Column 349 └──Row 350``` 351 352Example: 353 354In the following example, the **visibility** attribute is used to control the visibility of the **Image** component, avoiding the re-layout and re-rendered caused by **if/else**. 355 356```ts 357@Entry 358@Component 359struct TopicItem { 360 @State isVisible : Boolean = true; 361 362 build() { 363 Stack() { 364 Column(){ 365 Image($r('app.media.icon')) 366 .width('25%').height('12.5%').visibility(this.isVisible ? Visibility.Visible : Visibility.None) 367 Image($r('app.media.icon')) 368 .width('25%').height('12.5%').visibility(this.isVisible ? Visibility.Visible : Visibility.None) 369 Image($r('app.media.icon')) 370 .width('25%').height('12.5%').visibility(this.isVisible ? Visibility.Visible : Visibility.None) 371 Image($r('app.media.icon')) 372 .width('25%').height('12.5%').visibility(this.isVisible ? Visibility.Visible : Visibility.None) 373 } 374 Column() { 375 Row().width(300).height(200).backgroundColor(Color.Pink) 376 } 377 } 378 } 379} 380``` 381 382Note: The aforementioned recommendation is provided in regard of performance. If memory is more of a concern, use **if/else** instead. 383 384## Reducing Layout Time 385 386Compared with a flat layout, a deep layout with too much nesting means more time spent on node creation and layout. For optimized layout hierarchies, avoid redundant nesting or flatten the layout. 387 388Nonexample: 389 390The following uses a linear layout, which takes 166 ms 313 us. 391 392```ts 393 394import measure from '@ohos.measure'; 395import prompt from '@ohos.prompt'; 396 397@Entry 398@Component 399struct PerformanceRelative { 400 @State message: string = 'Hello World' 401 @State textWidth: string = ""; 402 403 build() { 404 Column() { 405 Image($r("app.media.app_icon")).width("100%").height(300).margin({ bottom: 20 }) 406 Row() { 407 Blank() 408 Column() { 409 Image($r("app.media.app_icon")).margin({ bottom: 4 }).width(40).aspectRatio(1) 410 Text("Name") 411 }.margin({ left: 8, right: 8 }) 412 }.position({ y: 280 }).width("100%") 413 // Empty row 414 Row().height(this.textWidth) 415 Column() { 416 Row() { 417 Text("Singapore").fontSize(20).fontWeight(FontWeight.Bolder) 418 .margin(8) 419 .textAlign(TextAlign.Start) 420 }.width("100%").justifyContent(FlexAlign.Start) 421 422 Flex({ alignItems: ItemAlign.Center }) { 423 Text("Camera").flexShrink(0) 424 .margin({ right: 8 }) 425 TextInput() 426 }.margin(8) 427 428 Flex({ alignItems: ItemAlign.Center }) { 429 Text("Settings").flexShrink(0) 430 .margin({ right: 8 }) 431 TextInput() 432 }.margin(8) 433 434 Row() { 435 Column() { 436 Image($r("app.media.app_icon")).width(80).aspectRatio(1).margin({ bottom: 8 }) 437 Text("Description") 438 }.margin(8) 439 440 Column() { 441 Text("Title").fontWeight(FontWeight.Bold).margin({ bottom: 8 }) 442 Text("Long Text") 443 }.margin(8).layoutWeight(1).alignItems(HorizontalAlign.Start) 444 }.margin(8).width("100%").alignItems(VerticalAlign.Top) 445 }.layoutWeight(1) 446 447 Flex({ justifyContent: FlexAlign.End }) { 448 Button("Upload").margin(8) 449 Button("Discard").margin(8) 450 } 451 } 452 .width("100%").height("100%") 453 } 454} 455 456``` 457 458Example: 459 460Use of a relative layout reduces both the nesting depth and quantity of components, shortening the time required for layout to 123 ms 278 us. 461 462```ts 463 464@Entry 465@Component 466struct RelativePerformance { 467 @State message: string = 'Hello World' 468 469 build() { 470 RelativeContainer(){ 471 Image($r("app.media.app_icon")) 472 .height(300) 473 .width("100%") 474 .id("topImage") 475 .alignRules({ 476 left: { anchor: "__container__", align: HorizontalAlign.Start }, 477 top: {anchor: "__container__", align: VerticalAlign.Top } 478 }) 479 Image($r("app.media.app_icon")) 480 .width(40) 481 .aspectRatio(1) 482 .margin(4) 483 .id("topCornerImage") 484 .alignRules({ 485 right: { anchor: "__container__", align: HorizontalAlign.End }, 486 center: {anchor: "topImage", align: VerticalAlign.Bottom } 487 }) 488 Text("Name") 489 .id("name") 490 .margin(4) 491 .alignRules({ 492 left: { anchor: "topCornerImage", align: HorizontalAlign.Start }, 493 top: {anchor: "topCornerImage", align: VerticalAlign.Bottom } 494 }) 495 Text("Singapore") 496 .margin(8) 497 .fontWeight(FontWeight.Bolder) 498 .fontSize(20) 499 .id("singapore") 500 .alignRules({ 501 left: { anchor: "__container__", align: HorizontalAlign.Start }, 502 top: {anchor: "name", align: VerticalAlign.Bottom } 503 }) 504 Text("Camera") 505 .margin(8) 506 .id("camera") 507 .alignRules({ 508 left: { anchor: "__container__", align: HorizontalAlign.Start }, 509 top: {anchor: "singapore", align: VerticalAlign.Bottom } 510 }) 511 TextInput() 512 .id("cameraInput") 513 .alignRules({ 514 left: { anchor: "camera", align: HorizontalAlign.End }, 515 right:{ anchor: "__container__", align: HorizontalAlign.End }, 516 top: {anchor: "camera", align: VerticalAlign.Top }, 517 bottom: { anchor: "camera", align: VerticalAlign.Bottom } 518 }) 519 Text("Settings") 520 .margin(8) 521 .id("settings") 522 .alignRules({ 523 left: { anchor: "__container__", align: HorizontalAlign.Start }, 524 top: {anchor: "camera", align: VerticalAlign.Bottom } 525 }) 526 TextInput() 527 .id("settingInput") 528 .alignRules({ 529 left: { anchor: "settings", align: HorizontalAlign.End }, 530 right:{ anchor: "__container__", align: HorizontalAlign.End }, 531 top: {anchor: "settings", align: VerticalAlign.Top }, 532 bottom: { anchor: "settings", align: VerticalAlign.Bottom } 533 }) 534 Image($r("app.media.app_icon")) 535 .id("descriptionIcon") 536 .margin(8) 537 .width(80) 538 .aspectRatio(1) 539 .alignRules({ 540 left: { anchor: "__container__", align: HorizontalAlign.Start }, 541 top: {anchor: "settings", align: VerticalAlign.Bottom } 542 }) 543 Text("Description") 544 .id("description") 545 .margin(8) 546 .alignRules({ 547 left: { anchor: "__container__", align: HorizontalAlign.Start }, 548 top: {anchor: "descriptionIcon", align: VerticalAlign.Bottom } 549 }) 550 Text("Title") 551 .fontWeight(FontWeight.Bold) 552 .id("title") 553 .margin(8) 554 .alignRules({ 555 left: { anchor: "description", align: HorizontalAlign.End }, 556 top: {anchor: "descriptionIcon", align: VerticalAlign.Top } 557 }) 558 Text("Long Text") 559 .id("longText") 560 .margin(8) 561 .alignRules({ 562 left: { anchor: "description", align: HorizontalAlign.End }, 563 right: { anchor: "__container__", align: HorizontalAlign.End }, 564 top: {anchor: "title", align: VerticalAlign.Bottom } 565 }) 566 Button("Discard") 567 .id("discard") 568 .margin(8) 569 .alignRules({ 570 right: { anchor: "__container__", align: HorizontalAlign.End }, 571 bottom: {anchor: "__container__", align: VerticalAlign.Bottom } 572 }) 573 Button("Upload") 574 .id("upload") 575 .margin(8) 576 .alignRules({ 577 right: { anchor: "discard", align: HorizontalAlign.Start }, 578 bottom: {anchor: "__container__", align: VerticalAlign.Bottom } 579 }) 580 }.width("100%").height("100%") 581 } 582} 583 584``` 585 586 587## Using Layout Optimization Tools 588 589[DevEco Studio](../quick-start/deveco-studio-user-guide-for-openharmony.md) has a built-in ArkUI Inspector tool, which you can use to inspect the UI display effect of your application running on a real device. With ArkUI Inspector, you can quickly identify layout and other UI-related issues, as well as inspect the layout relationships and attributes of components. 590