HarmonyOS鸿蒙Next中如何解决改变@Provide修饰的值,对应UI未刷新的问题
HarmonyOS鸿蒙Next中如何解决改变@Provide修饰的值,对应UI未刷新的问题
【问题现象】
通过@Provide修饰了一个对象,在Class里改变Provide的值,发现UI监听不到,页面上的数据未发生改变。
问题现象如下图:
【背景知识】
在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个UI模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。
自定义组件拥有变量,变量必须被装饰器装饰才可以成为状态变量,状态变量的改变会引起UI的渲染刷新。如果不使用状态变量,UI只能在初始化时渲染,后续将不会再刷新。下图展示了State和View(UI)之间的关系。
- View(UI):UI渲染,是指把build方法内的UI描述和@Builder装饰的方法内的UI描述映射到界面。
- State:状态,指驱动UI更新的数据。用户通过触发组件的事件方法,改变状态数据。状态数据的改变,将引起UI的重新渲染。
- 状态变量:被状态装饰器装饰的变量,状态变量值的改变会引起UI的渲染更新。示例:@State num: number = 1,其中@State是状态装饰器,num是状态变量。
- 常规变量:没有被状态装饰器装饰的变量,通常应用于辅助计算。它的改变永远不会引起UI的刷新。
【定位思路】
相关问题代码如下:
// Index.ets
@Entry
@Component
struct Index {
[@Provide](/user/Provide)('pageStack') pageStack: NavPathStack = new NavPathStack()
[@Provide](/user/Provide)('playInfo') playInfo: PlayInfo = currentPlayInfo
build() {
Navigation(this.pageStack) {
Column() {
if (this.playInfo.isPlaying) {
Text("正在播放" + this.playInfo.title)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
} else {
Text("没有播放")
.fontSize(30)
}
Text("Change PlayInfo")
.fontSize(30)
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
.onClick(() => {
player.play();
})
}
.height('100%')
.width('100%')
}
.hideTitleBar(true)
}
}
// PlayInfo.ets
export class PlayInfo {
isPlaying: boolean = false
title: string = ""
}
let currentPlayInfo = new PlayInfo();
export default currentPlayInfo as PlayInfo;
// AVPlayer.ets
import currentPlayInfo from './PlayInfo'
class AVPlayer {
play(){
currentPlayInfo.isPlaying = !currentPlayInfo.isPlaying
currentPlayInfo.title = "第" + Math.round(Math.random()*100) + "首"
}
}
let player = new AVPlayer();
export default player as AVPlayer;
如上代码,定义了一个播放类PlayInfo,同时new出了一个单例对象currentPlayInfo并导出给Index.ets和AVPlayer.ets使用,在Index.ets中通过@Provide声明了一个状态变量PlayInfo并把currentPlayInfo赋值给PlayInfo,点击文本时通过触发AVPlayer的play方法改变单例对象currentPlayInfo,预期能触发页面的刷新,实际上页面内容未发生改变。
如“背景知识”一章所述,ArkUI编程框架中,页面刷新是需要通过状态装饰器装饰的状态变量,由状态变量值的改变引起UI的渲染更新。实际上,对于@Provide装饰的状态变量,会被代理变成一个Proxy对象,并不是原先的currentPlayInfo对象,因此在AVPlayer类中修改的对象,与Index页面中的对象不一致,play函数虽然修改了变量但是修改的是未被代理前的对象,@Provide修饰的playInfo状态变量无法监听到变化,因而无法驱动UI的改变。
【解决方案】
对于状态变量,如果需要驱动UI的刷新,只需要修改该状态变量即可。如上代码可以做下述修改,即可完成数据驱动UI的改变:
// Index.ets
Text("Change PlayInfo")
.fontSize(30)
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
.onClick(() => {
// 需要修改的是状态变量,以实现驱动UI更新的效果
player.playNew(this.playInfo as PlayInfo);
})
// AVPlayer.ets
class AVPlayer {
playNew(playInfo: PlayInfo) {
playInfo.isPlaying = !playInfo.isPlaying
playInfo.title = "第" + Math.round(Math.random()*100) + "首"
}
}
修改后的效果图如下:
【总结】
- ArkUI是基于MVVM模式的声明式UI的编程框架,通过状态变量的变化驱动UI的更新,因此在需要更新UI时,需要修改对应的状态变量。
- 对于未被装饰符修饰的变量或者对象等,主要是用于类型声明以及初始化,修改该变量无法直接引起UI数据的刷新。
更多关于HarmonyOS鸿蒙Next中如何解决改变@Provide修饰的值,对应UI未刷新的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于HarmonyOS鸿蒙Next中如何解决改变@Provide修饰的值,对应UI未刷新的问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,@Provide
修饰的状态变量会被代理为Proxy
对象。直接修改未被代理的对象(如currentPlayInfo
)不会触发UI刷新。要解决此问题,需直接修改@Provide
修饰的状态变量。例如,在Index.ets
中,通过player.playNew(this.playInfo)
修改playInfo
,确保状态变量变化驱动UI更新。