跳到主要内容

UI显示异常调试

本章节主要介绍UI显示异常问题的调试方法,并结合案例讲解具体的解决步骤。

定位UI显示异常问题

UI显示异常问题主要是通过分析UI布局信息来定位。当前分析UI布局主要通过getInspectorTree接口获取组件树信息,或者通过getRectangleById接口获取单个节点的信息。

组件树

从API version 9开始,可以使用getInspectorTree接口获取组件树及其属性。

单个节点

从API version 10开始,可以使用getRectangleById接口获取组件的大小、位置、平移、缩放、旋转及仿射矩阵等属性信息。

解决UI显示异常问题

下面通过具体案例,介绍如何解决UI显示异常问题。

通过ComponentUtils.getRectangleById获取的tabBar组件坐标尺寸异常

问题现象

在动态控制tabBar显示或隐藏的场景下,通过ComponentUtils.getRectangleById获取的tabBar组件坐标或尺寸可能与预期不符。例如,当tabBar隐藏时(宽度设为0),获取的坐标位于屏幕中央,恢复显示后,该错误坐标仍被沿用。

可能原因

  • 使用同步接口查询布局信息时,目标节点的宽度临时设置为0,节点布局默认居中显示,导致获取的坐标位于屏幕中央。
  • 调用接口时,如果当前布局尚未完成渲染(例如,组件刚被隐藏或显示,布局计算未结束),查询到的将是未更新的旧布局信息。

解决措施

  • 选择合适的调用时机:在组件完成布局渲染后调用接口。例如,tabBar恢复显示后,使用延迟函数等待布局更新完成,再获取坐标。
  • 监听布局变化事件:利用组件的onAreaChange回调,在布局变化并稳定后,触发坐标获取逻辑。
  • 增加有效性校验:获取坐标后,校验组件尺寸,过滤无效数据。宽度或高度为0的组件被视为无效。

代码示例

import { ComponentUtils } from '@kit.ArkUI';

@Entry
@Component
struct Page {
@State currentIndex: number = 0;
@State msg: string = 'info';
@State pivotX: number = 0;
@State pivotY: number = 0;
@State pivotShow: boolean = false;
@State tabBarShow: boolean = true;

private controller : TabsController = new TabsController();
private uiContext : UIContext | undefined = undefined;
private componentUtils : ComponentUtils | undefined = undefined;
private componentId : string = 'tab-pink';
private flag : boolean = false;
private baseX : number = 0;
private baseY : number = 0;

@Builder
tabBuilder(index: number, name: string) {
Column() {
Text(name)
.fontSize(16)
.fontWeight(this.currentIndex === index ? 500 : 400)
.fontColor(this.currentIndex === index ? '#007DFF': '#182431')
.lineHeight(22)
}
.id(`tab-${name}`)
.width('100%')
.height('100%')
.borderStyle(BorderStyle.Solid)
.borderWidth(1)
}

aboutToAppear(): void {
this.uiContext = this.getUIContext();
this.componentUtils = this.getUIContext().getComponentUtils();
}

getRectInfo(id?: string) : string {
let componentId : string = id??this.componentId;
let info = this.componentUtils?.getRectangleById(componentId);
let infoStr : string = '';
if (info) {
infoStr = 'Size: ' + JSON.stringify(info.size) + ', WindowOffset: ' + JSON.stringify(info.windowOffset);
}
return infoStr;
}

getBasePosition() : void {
if (this.flag) {
return;
}
let info = this.componentUtils?.getRectangleById('root-stack');
if (info) {
this.baseX = info.windowOffset.x;
this.baseY = info.windowOffset.y;
this.msg = `${this.componentId}: ` + this.getRectInfo(this.componentId) + `, pivot: {x: ${this.pivotX}, y: ${this.pivotY}}`;
this.flag = true;
}
}

onDidBuild(): void {
}

build() {
Stack() {
Column() {
Text(this.msg)
.fontSize(20)
.border({ width: 5, color: Color.Brown })
.width('100%')
.height('30%')
.margin({ top: 50 })
Row() {
Button('Rect')
.onClick(() => {
this.msg = JSON.stringify(this.componentUtils?.getRectangleById('tab-pink'))
})
.width('33%')
Button('replay')
.onClick(() => {
this.pivotShow = false;
this.tabBarShow = false;
this.pivotShow = true;
setTimeout(() => {
this.tabBarShow = true
}, 100)
})
.width('33%')
Button('pivot')
.onClick(() => {
this.pivotShow = !this.pivotShow;
})
.width('33%')
}
.width('100%')
.height('10%')
.justifyContent(FlexAlign.SpaceEvenly)
Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) {
TabContent() {
Column()
.width('100%')
.height('100%')
.backgroundColor('#00CB87')
}
.tabBar(this.tabBuilder(0, 'green'))
TabContent() {
Column()
.width('100%')
.height('100%')
.backgroundColor('#007DFF')
}
.tabBar(this.tabBuilder(1, 'blue'))
TabContent() {
Column()
.width('100%')
.height('100%')
.backgroundColor('#FFBF00')
}
.tabBar(this.tabBuilder(2, 'yellow'))
.width('25%')
TabContent() {
Column()
.width('100%')
.height('100%')
.backgroundColor('#E67C92')
}
.tabBar(this.tabBuilder(3, 'pink'))
}
.expandSafeArea([SafeAreaType.CUTOUT, SafeAreaType.SYSTEM, SafeAreaType.KEYBOARD],
[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
.barWidth(this.tabBarShow ? '100%' : 0)
.width('100%')
.height('40%')
.barHeight(44)
.vertical(false)
.barMode(BarMode.Fixed)
.backgroundColor('#F1F2F3')
.onChange((index: number) => {
this.currentIndex = index;
if (index == 3) {
this.pivotShow = false;
}
})
.animation({ duration: 100, curve: Curve.Linear })
}
.id('col')
.width('100%')
.height('100%')
.justifyContent(FlexAlign.SpaceBetween)
if (this.pivotShow) {
Text('X')
.width(18)
.height(18)
.textAlign(TextAlign.Center)
.borderRadius(9)
.fontColor(Color.White)
.backgroundColor(Color.Red)
.position({ x: this.uiContext?.px2vp(this.pivotX), y: this.uiContext?.px2vp(this.pivotY) })
.onAreaChange(() => {
let info = this.componentUtils?.getRectangleById(this.componentId);
if (info) {
this.getBasePosition();
this.pivotX = info.windowOffset.x - this.baseX;
this.pivotY = info.windowOffset.y - this.baseY;
this.msg = `${this.componentId}: ` + this.getRectInfo(this.componentId) + `, pivot: {x: ${this.pivotX}, y: ${this.pivotY}}`;
}
})
}
}
.id('root-stack')
}
}