1# UI显示异常调试 2 3本章节主要介绍UI显示异常问题的调试方法,并结合案例讲解具体的解决步骤。 4 5## 定位UI显示异常问题 6 7UI显示异常问题主要是通过分析UI布局信息来定位。当前分析UI布局主要通过getInspectorTree接口获取组件树信息,或者通过getRectangleById接口获取单个节点的信息。 8 9**组件树** 10 11从API version 9开始,可以使用[getInspectorTree](../reference/apis-arkui/arkui-ts/ts-universal-attributes-component-id.md#getinspectortree9)接口获取组件树及其属性。 12 13**单个节点** 14 15从API version 10开始,可以使用[getRectangleById](../reference/apis-arkui/arkts-apis-uicontext-componentutils.md#getrectanglebyid)接口获取组件的大小、位置、平移、缩放、旋转及仿射矩阵等属性信息。 16 17## 解决UI显示异常问题 18 19下面通过具体案例,介绍如何解决UI显示异常问题。 20 21### 通过ComponentUtils.getRectangleById获取的tabBar组件坐标尺寸异常 22 23**问题现象** 24 25在动态控制tabBar显示或隐藏的场景下,通过`ComponentUtils.getRectangleById`获取的tabBar组件坐标或尺寸可能与预期不符。例如,当tabBar隐藏时(宽度设为0),获取的坐标位于屏幕中央,恢复显示后,该错误坐标仍被沿用。 26 27**可能原因** 28 29- 使用同步接口查询布局信息时,目标节点的宽度临时设置为0,节点布局默认居中显示,导致获取的坐标位于屏幕中央。 30- 调用接口时,如果当前布局尚未完成渲染(例如,组件刚被隐藏或显示,布局计算未结束),查询到的将是未更新的旧布局信息。 31 32**解决措施** 33 34- 选择合适的调用时机:在组件完成布局渲染后调用接口。例如,tabBar恢复显示后,使用延迟函数等待布局更新完成,再获取坐标。 35- 监听布局变化事件:利用组件的`onAreaChange`回调,在布局变化并稳定后,触发坐标获取逻辑。 36- 增加有效性校验:获取坐标后,校验组件尺寸,过滤无效数据。宽度或高度为0的组件被视为无效。 37 38 39**代码示例** 40 41```ts 42import { ComponentUtils } from '@kit.ArkUI'; 43 44@Entry 45@Component 46struct Page { 47 @State currentIndex: number = 0; 48 @State msg: string = 'info'; 49 @State pivotX: number = 0; 50 @State pivotY: number = 0; 51 @State pivotShow: boolean = false; 52 @State tabBarShow: boolean = true; 53 54 private controller : TabsController = new TabsController(); 55 private uiContext : UIContext | undefined = undefined; 56 private componentUtils : ComponentUtils | undefined = undefined; 57 private componentId : string = 'tab-pink'; 58 private flag : boolean = false; 59 private baseX : number = 0; 60 private baseY : number = 0; 61 62 @Builder 63 tabBuilder(index: number, name: string) { 64 Column() { 65 Text(name) 66 .fontSize(16) 67 .fontWeight(this.currentIndex === index ? 500 : 400) 68 .fontColor(this.currentIndex === index ? '#007DFF': '#182431') 69 .lineHeight(22) 70 } 71 .id(`tab-${name}`) 72 .width('100%') 73 .height('100%') 74 .borderStyle(BorderStyle.Solid) 75 .borderWidth(1) 76 } 77 78 aboutToAppear(): void { 79 this.uiContext = this.getUIContext(); 80 this.componentUtils = this.getUIContext().getComponentUtils(); 81 } 82 83 getRectInfo(id?: string) : string { 84 let componentId : string = id??this.componentId; 85 let info = this.componentUtils?.getRectangleById(componentId); 86 let infoStr : string = ''; 87 if (info) { 88 infoStr = 'Size: ' + JSON.stringify(info.size) + ', WindowOffset: ' + JSON.stringify(info.windowOffset); 89 } 90 return infoStr; 91 } 92 93 getBasePosition() : void { 94 if (this.flag) { 95 return; 96 } 97 let info = this.componentUtils?.getRectangleById('root-stack'); 98 if (info) { 99 this.baseX = info.windowOffset.x; 100 this.baseY = info.windowOffset.y; 101 this.msg = `${this.componentId}: ` + this.getRectInfo(this.componentId) + `, pivot: {x: ${this.pivotX}, y: ${this.pivotY}}`; 102 this.flag = true; 103 } 104 } 105 106 onDidBuild(): void { 107 } 108 109 build() { 110 Stack() { 111 Column() { 112 Text(this.msg) 113 .fontSize(20) 114 .border({ width: 5, color: Color.Brown }) 115 .width('100%') 116 .height('30%') 117 .margin({ top: 50 }) 118 Row() { 119 Button('Rect') 120 .onClick(() => { 121 this.msg = JSON.stringify(this.componentUtils?.getRectangleById('tab-pink')) 122 }) 123 .width('33%') 124 Button('replay') 125 .onClick(() => { 126 this.pivotShow = false; 127 this.tabBarShow = false; 128 this.pivotShow = true; 129 setTimeout(() => { 130 this.tabBarShow = true 131 }, 100) 132 }) 133 .width('33%') 134 Button('pivot') 135 .onClick(() => { 136 this.pivotShow = !this.pivotShow; 137 }) 138 .width('33%') 139 } 140 .width('100%') 141 .height('10%') 142 .justifyContent(FlexAlign.SpaceEvenly) 143 Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) { 144 TabContent() { 145 Column() 146 .width('100%') 147 .height('100%') 148 .backgroundColor('#00CB87') 149 } 150 .tabBar(this.tabBuilder(0, 'green')) 151 TabContent() { 152 Column() 153 .width('100%') 154 .height('100%') 155 .backgroundColor('#007DFF') 156 } 157 .tabBar(this.tabBuilder(1, 'blue')) 158 TabContent() { 159 Column() 160 .width('100%') 161 .height('100%') 162 .backgroundColor('#FFBF00') 163 } 164 .tabBar(this.tabBuilder(2, 'yellow')) 165 .width('25%') 166 TabContent() { 167 Column() 168 .width('100%') 169 .height('100%') 170 .backgroundColor('#E67C92') 171 } 172 .tabBar(this.tabBuilder(3, 'pink')) 173 } 174 .expandSafeArea([SafeAreaType.CUTOUT, SafeAreaType.SYSTEM, SafeAreaType.KEYBOARD], 175 [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) 176 .barWidth(this.tabBarShow ? '100%' : 0) 177 .width('100%') 178 .height('40%') 179 .barHeight(44) 180 .vertical(false) 181 .barMode(BarMode.Fixed) 182 .backgroundColor('#F1F2F3') 183 .onChange((index: number) => { 184 this.currentIndex = index; 185 if (index == 3) { 186 this.pivotShow = false; 187 } 188 }) 189 .animation({ duration: 100, curve: Curve.Linear }) 190 } 191 .id('col') 192 .width('100%') 193 .height('100%') 194 .justifyContent(FlexAlign.SpaceBetween) 195 if (this.pivotShow) { 196 Text('X') 197 .width(18) 198 .height(18) 199 .textAlign(TextAlign.Center) 200 .borderRadius(9) 201 .fontColor(Color.White) 202 .backgroundColor(Color.Red) 203 .position({ x: this.uiContext?.px2vp(this.pivotX), y: this.uiContext?.px2vp(this.pivotY) }) 204 .onAreaChange(() => { 205 let info = this.componentUtils?.getRectangleById(this.componentId); 206 if (info) { 207 this.getBasePosition(); 208 this.pivotX = info.windowOffset.x - this.baseX; 209 this.pivotY = info.windowOffset.y - this.baseY; 210 this.msg = `${this.componentId}: ` + this.getRectInfo(this.componentId) + `, pivot: {x: ${this.pivotX}, y: ${this.pivotY}}`; 211 } 212 }) 213 } 214 } 215 .id('root-stack') 216 } 217} 218```