• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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```