SystemUI状态栏每次下拉背景图不一致问题分析报告
1 关键字
system_ui;状态栏;下拉背景;
2 问题描述
设备型号:dayu200
系统版本:OpenHarmony 3.1 Release
代码版本:OpenHarmony-v3.1-Release
问题现象:顶部状态栏下拉时,下拉的页面有个背景图遮住了屏幕主页面,多次下拉每次的背景图都会产生变化,最终会导致屏幕闪烁。
3 问题原因
3.1 正常机制
每次下拉顶部状态栏,下拉界面的背景图应该保持一致。
3.2 异常机制
顶部状态栏多次下拉时,下拉背景图会变化。
4 解决方案
-
创建新的@State showBackground数据作为背景显示的判断依据
//\applications_systemui\product\phone\dropdownpanel\src\main\ets\pages\index.ets
@Entry
@Component
struct Index {
...
@State mBackground: PixelMap | undefined = undefined;
// 在mBackground下方定义boolean类型数据showBackground,默认值给false
@State showBackground: boolean = false;
...
}
-
在hideSelf方法中修改 showBackground 状态
// \applications_systemui\product\phone\dropdownpanel\src\main\ets\pages\index.ets
...
hideSelf() {
Log.showInfo(TAG, `hideSelf`)
this._animateTo({ ...SHOW_ANIM_CONFIG, onFinish: () => {
Log.showInfo(TAG, `hideSelf, hide anim finish.`);
this.showComponentName = undefined
this.mBackground && this.mBackground.release();
this.mBackground = undefined;
// 在页面隐藏并清除背景图片时将showBackground设置为false
this.showBackground = false;
WindowManager.hideWindow(WindowType.DROPDOWN_PANEL)
}}, () => {
this.componentOptAreaTranslateY = (-this.componentOptAreaHeightPX * 0.1) + 'px';
this.backgroundOpacity = 0;
})
}
-
在updateBackground方法中修改 showBackground 状态
// \applications_systemui\product\phone\dropdownpanel\src\main\ets\pages\index.ets
...
updateBackground() {
let rect = WindowManager.getWindowInfo(WindowType.DROPDOWN_PANEL)?.rect;
Log.showInfo(TAG, "start get snapShot, rect:" + JSON.stringify(rect));
screenshot.save({ screenRect: rect }).then((snapImage) => {
Log.showInfo(TAG, "get snap: + JSON.stringify(snapImage)")
this.mBackground = snapImage;
// 在更新背景图片时,将showBackground设置为true
this.showBackground = true;
})
}
-
在build中修改背景图片判断的逻辑
// \applications_systemui\product\phone\dropdownpanel\src\main\ets\pages\index.ets
...
// 将显示背景图片的判断依据由mBackground改为showBackground
if (this.showBackground) {
Image(this.mBackground)
.width('100%')
.height('100%')
.objectFit(ImageFit.Fill)
.blur(20)
.opacity(this.backgroundOpacity)
}
5 定位过程
-
分析背景图片mBackground是在何时生成,在updateBackground方法中,通过screenshot.save方法设置了mBackground。
updateBackground() {
let rect = WindowManager.getWindowInfo(WindowType.DROPDOWN_PANEL) ? .rect;
Log.showInfo(TAG, "start get snapShot, rect:"+JSON.stringify(rect));
// screenshot.save方法是对指定的窗口区域尺寸截图,由此判断出背景图片是主界面的截图。
screenshot.save({ screenRect: rect }).then((snapImage) => {
Log.showInfo(TAG, "get snap:"+ JSON.stringify(snapImage))
// 设置背景图片
this.mBackground = snapImage
})
}
-
通过hideSelf方法可以判断出,页面隐藏后将mBackground设置为undefined。
hideSelf() {
Log.showInfo(TAG, "hideSelf")
this._animateTo({...SHOW_ANIM_CONFIG,
onFinish: () => {
Log.showInfo(TAG, "hideSelf, hide anim finish.");
this.showComponentName = undefined
this.mBackground && this.mBackground.release();
// 将背景图片设置为undefined
this.mBackground = undefined;
WindowManager.hideWindow(WindowType.DROPDOWN_PANEL)
}
}, () => {
this.componentOptAreaTranslateY = (-this.componentOptAreaHeightPX * 0.1) + 'px';
this.backgroundOpacity = 0
})
}
-
按照以上逻辑,在下拉界面时 mBackground 为 undefined, 然后下拉时通过 screenshot.save 截图功能将主界面的截图作为下拉界面的背景图,下拉页面收起隐藏的时,将 mBackground 设置为undefined 清除背景图。但每次下拉的背景不一样,初步判断为截图时出了问题,因此查看每次下拉时updateBackground的日志。
08-05 11:01:02.515 1382-1382/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> start get snapShot, rect: {"left":0,"top":0,"width":720,"height":1208} 08-05 11:01:02.779 1382-1382/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> get snap: {"_napiwrapper":{}} 08-05 11:01:04.577 1382-1382/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> start get snapShot, rect: {"left":0,"top":0,"width":720,"height":1208} 08-05 11:01:04.819 1382-1382/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> get snap: {"_napiwrapper":{}} 08-05 11:01:06.404 1382-1382/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> start get snapShot, rect: {"left":0,"top":0,"width":720,"height":1208} 08-05 11:01:06.669 1382-1382/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> get snap: {"_napiwrapper":{}}
查看日志发现每次截图screenshot.save的入参screenRect和返回值是一样的。判断截图方法在执行上没有问题,但是截出来的图片与上次的不一样,由此推测截图时页面上有其他东西,但下拉界面除了背景图片外并没有设置其他的东西,背景图片也在下拉界面隐藏是被清除了。
-
为了排除下拉背景图片的影响,在hideSelf和updateBackground中分别添加日志:
// hideSelf 在设置mBackground 为undefined之后添加日志打印mBackground的值
hideSelf() {
Log.showInfo(TAG, "hideSelf");
this._animateTo({...SHOW_ANIM_CONFIG,
onFinish: () => {
Log.showInfo(TAG, "hideSelf, hide anim finish.");
this.showComponentName = undefined
this.mBackground && this.mBackground.release();
// 将背景图片设置为undefined
this.mBackground = undefined;
// 添加打印日志
Log.showInfo(TAG, "hideSelf, hide anim finish mBackground:"+JSON.stringify(this.mBackground));
WindowManager.hideWindow(WindowType.DROPDOWN_PANEL)
}
}, () => {
this.componentOptAreaTranslateY = (-this.componentOptAreaHeightPX * 0.1) + 'px';
this.backgroundOpacity = 0
})
}
// updateBackground 在screenshot.save截图之前打印 mBackground 的值
updateBackground() {
let rect = WindowManager.getWindowInfo(WindowType.DROPDOWN_PANEL) ? .rect;
Log.showInfo(TAG, "start get snapShot, rect:" + JSON.stringify(rect))
// 添加打印日志
Log.showInfo(TAG, "start get snapShot mBackground:" + JSON.stringify(this.mBackground));
screenshot.save({ screenRect: rect }).then((snapImage) => {
Log.showInfo(TAG, "get snap:" + JSON.stringify(snapImage))
// 设置背景图片
this.mBackground = snapImage
})
}
日志如下:
// 第一次下拉截图 updateBackground mBackground:undefined -- 正确,截图时没有背景图 08-05 11:20:24.047 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> start get snapShot mBackground:undefined 08-05 11:20:24.295 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> screenshot get snapShot mBackground :{"_napiwrapper":{}} // 第一次收起清除图片 mBackground:{"_napiwrapper":{}} -- 错误,this.mBackground = undefined;执行后并没有清除掉mBackground 08-05 11:20:28.974 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> hideSelf, hide anim finish mBackground:{"_napiwrapper":{}} // 第二次下拉截图 updateBackground mBackground:{"_napiwrapper":{}} -- 错误,截图时存在mBackground背景图片 08-05 11:20:33.835 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> start get snapShot mBackground:{"_napiwrapper":{}} 08-05 11:20:34.077 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> screenshot get snapShot mBackground :{"_napiwrapper":{}} // 第二次收起清除图片 mBackground:{"_napiwrapper":{}} -- 错误,this.mBackground = undefined;执行后并没有清除掉mBackground 08-05 11:20:37.678 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> hideSelf, hide anim finish mBackground:{"_napiwrapper":{}} // 第三次下拉截图 updateBackground mBackground:{"_napiwrapper":{}} -- 错误,截图时存在mBackground背景图片 08-05 11:20:40.986 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> start get snapShot mBackground:{"_napiwrapper":{}} 08-05 11:20:41.223 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> screenshot get snapShot mBackground :{"_napiwrapper":{}} // 第三次收起清除图片 mBackground:{"_napiwrapper":{}} -- 错误,this.mBackground = undefined;执行后并没有清除掉mBackground 08-05 11:20:43.682 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> hideSelf, hide anim finish mBackground:{"_napiwrapper":{}} // 第四次下拉截图 updateBackground mBackground:{"_napiwrapper":{}} -- 错误,截图时存在mBackground背景图片 08-05 11:20:45.873 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> start get snapShot mBackground:{"_napiwrapper":{}} 08-05 11:20:46.119 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> screenshot get snapShot mBackground :{"_napiwrapper":{}} // 第四次收起清除图片 mBackground:{"_napiwrapper":{}} -- 错误,this.mBackground = undefined;执行后并没有清除掉mBackground 08-05 11:20:48.237 1396-1396/com.ohos.systemui I 02200/JsApp: SystemUI_Default tag: DropdownPanel-Index --> hideSelf, hide anim finish mBackground:{"_napiwrapper":{}}
-
根据上述日志判断,每次收起下拉界面执行
this.mBackground = undefined;
之后打印的mBackground 发现还有值存在,导致每次截图时,上一次的截图一直存在,多次截图时图片显示就不一样。但是this.mBackground = undefined;
这行代码没有生效是造成mBackground 未被清除的主要原因。查看mBackground 的定义。
@State mBackground: PixelMap | undefined = undefined;
mBackground可以是两种数据类型,分别是PixelMap和undefined,PixelMap本质是一个记录同图片像素信息的对象,查看编译后的代码:
// \applications_systemui\product\phone\dropdownpanel\build\default\intermediates\assets\default\ets\pages\index.js
class Index extends View {
constructor(compilerAssignedUniqueChildId, parent, params) {
super(compilerAssignedUniqueChildId, parent);
...
this.__mBackground = new ObservedPropertyObject(undefined, this, "mBackground");
...
}
}
mBackground最终调用的是ObservedPropertyObject方法进行的数据代理。 查看底层数据设置的代码:/foundation/ace/ace_engine/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/observed_property_object.ts 。
class ObservedPropertyObject < T extends Object > extends ObservedPropertyObjectAbstract < T > implements ISinglePropertyChangeSubscriber < T > {
private wrappedValue_: T;
constructor(
value: T,
owningView: IPropertySubscriber,
propertyName: PropertyInfo
) {
super(owningView, propertyName);
this.setValueInternal(value);
}
private setValueInternal(newValue: T): boolean {
// 当判断设置的值不为object类型时不进行操作
if (typeof newValue !== "object") {
console.debug("ObservedPropertyObject["+this.id__()+", " + this.info() + "] new value is NOT an object. Application error. Ignoring set.");
return false;
}
this.unsubscribeFromOwningProperty();
if (ObservedObject.IsObservedObject(newValue)) {
console.debug("ObservedPropertyObject["+this.id__()+", " + this.info() + "] new value is an ObservedObject already");
ObservedObject.addOwningProperty(newValue, this);
this.wrappedValue_ = newValue;
} else if (newValue instanceof SubscribaleAbstract) {
console.debug("ObservedPropertyObject[" + this.id__()+", " + this.info() + "] new value is an SubscribaleAbstract, subscribiung to it.");
this.wrappedValue_ = newValue;
((this.wrappedValue_ as unknown) as SubscribaleAbstract).addOwningProperty(this);
} else {
console.debug("ObservedPropertyObject["+ this.id__() + ", " + this.info() + "] new value is an Object, needs to be wrapped in an ObservedObject.");
this.wrappedValue_ = ObservedObject.createNew(newValue, this);
}
return true;
}
// 赋值时调用了setValueInternal方法
public set(newValue: T): void {
if (this.wrappedValue_ == newValue) {
console.debug("ObservedPropertyObject[" + this.id__()} + ", " + this.info() + "]: set with unchanged value - ignoring.");
return;
}
console.debug("ObservedPropertyObject[" + this.id__() + ", " + this.info() + "]: set, changed");
this.setValueInternal(newValue);
this.notifyHasChanged(newValue);
}
}
通过上面代码可得知,在设置this.mBackground 为 undefined时,会调用setValueInternal方法,在 setValueInternal 方法中判断了其数据类型不为 object 时不进行操作,因此在hideSelf方法中this.mBackground = undefined;
代码不会执行,从而导致背景图片一直没有被清除而造成了每次截图的结果不一样。
6 知识分享
在OpenHarmony的组件开发中,使用@state时需注意其数据类型的设置,建议不要选择两种不同的数据类型作为@State修饰的数据。
更多推荐
所有评论(0)