前言
Context是鸿蒙中及其重要的Api包括了非常多的接口和功能,而且在官方文档中也是优先推荐使用Context中的接口
在Stage模型中基本介绍
1. UI实例与窗口的一一关联
- 在Stage模型中,
WindowStage
或Window
是用于管理窗口的类,它们通过loadContent
接口加载页面内容。 - 当调用
loadContent
时,系统会创建一个UI实例,这个实例是页面内容的具体表现形式,包括布局、控件、样式等。 - 这个UI实例被绑定到调用
loadContent
的窗口上,因此每个窗口都有一个独立的UI实例与之关联。这种一一对应的关系确保了窗口的内容和行为是独立且可管理的。
2. 全局UI接口与UI实例的上下文关联
- 在Stage模型中,有一些全局的UI接口(例如修改UI样式、更新布局、触发事件等)。
- 这些接口的执行依赖于具体的UI实例上下文。所谓“上下文”,是指当前UI实例的状态、布局、控件等信息。
- 当调用这些全局UI接口时,系统会通过调用链(即代码的执行路径)追溯到当前的UI实例上下文。例如,如果在一个按钮点击事件中调用全局UI接口,系统会根据事件的来源(按钮所在的UI实例)找到对应的上下文。
3. 非UI页面或异步回调中的问题
- 在非UI页面(例如纯逻辑代码模块、工具类等)中调用全局UI接口时,由于这些代码没有直接关联到某个UI实例,系统无法通过调用链找到对应的上下文。
- 在异步回调中(例如定时器回调、网络请求完成后的回调等),调用全局UI接口也可能出现问题。因为异步回调的执行时机和上下文可能与最初调用时的上下文不一致。
- 如果系统无法找到正确的UI实例上下文,就会导致全局UI接口无法正确执行,从而引发接口执行失败。
4. 解决方法
为了避免这种问题,可以采用以下方法:
- 在UI页面中调用全局接口:确保全局UI接口的调用发生在与UI实例直接相关的代码中。
- 手动传递上下文:在异步回调中,可以手动将UI实例的上下文传递给回调函数,以便在回调中正确调用全局UI接口。
- 使用绑定机制:某些框架支持将异步回调与UI实例绑定,确保回调执行时能够正确找到上下文。
接下来将举一个简单的例子
// 定义一个名为 AnimateToExample 的组件,并标记为入口组件 (@Entry) 和可复用组件 (@Component)
@Entry
@Component
struct AnimateToExample {// 定义状态变量 widthSize,用于控制按钮的宽度,默认值为 250@State widthSize: number = 250// 定义状态变量 heightSize,用于控制按钮的高度,默认值为 100@State heightSize: number = 100// 定义状态变量 rotateAngle,用于控制旋转角度,默认值为 0@State rotateAngle: number = 0// 定义一个布尔型私有变量 flag,用于切换动画状态private flag: boolean = true// 定义一个 UIContext 类型的变量 uiContext,用于存储当前的 UI 上下文uiContext: UIContext | undefined = undefined;// aboutToAppear 方法:在组件即将显示时调用aboutToAppear() {// 获取当前的 UI 上下文并赋值给 uiContextthis.uiContext = this.getUIContext();if (!this.uiContext) {// 如果未成功获取 UI 上下文,则打印警告信息console.warn("no uiContext");return;}}// build 方法:定义组件的 UI 结构build() {Column() { // 创建一个垂直布局容器Button('change size') // 创建一个按钮,文本为 "change size".width(this.widthSize) // 设置按钮宽度为状态变量 widthSize.height(this.heightSize) // 设置按钮高度为状态变量 heightSize.margin(30) // 设置外边距为 30.onClick(() => { // 点击按钮时触发以下逻辑if (this.flag) { // 如果 flag 为 truethis.uiContext?.animateTo({ // 调用UIContext 的 animateTo 方法执行动画duration: 2000, // 动画持续时间为 2000 毫秒curve: Curve.EaseOut, // 动画曲线为 EaseOutiterations: 1, // 动画循环次数为 1 次playMode: PlayMode.Normal, // 动画播放模式为 NormalonFinish: () => { // 动画结束时回调console.info('play end') // 打印日志 "play end"}}, () => { // 动画更新逻辑this.widthSize = 150 // 修改按钮宽度为 150this.heightSize = 60 // 修改按钮高度为 60})} else { // 如果 flag 为 falsethis.uiContext?.animateTo({}, () => { // 调用 animateTo 方法重置按钮大小this.widthSize = 250 // 修改按钮宽度为 250this.heightSize = 100 // 修改按钮高度为 100})}this.flag = !this.flag // 切换 flag 的值})Button('stop rotating') // 创建另一个按钮,文本为 "stop rotating".margin(50) // 设置外边距为 50.rotate({ x: 0, y: 0, z: 1, angle: this.rotateAngle }) // 设置按钮的旋转属性.onAppear(() => { // 组件出现时触发以下逻辑// 开始无限循环的旋转动画this.uiContext?.animateTo({// 调用UIContext 的 animateTo 方法执行动画duration: 1200, // 动画持续时间为 1200 毫秒curve: Curve.Friction, // 动画曲线为 Frictiondelay: 500, // 动画延迟 500 毫秒后开始iterations: -1, // 动画无限循环(-1 表示无限)playMode: PlayMode.Alternate, // 动画播放模式为 Alternate(来回交替)expectedFrameRateRange: { // 设置期望的帧率范围min: 10, // 最小帧率为 10max: 120, // 最大帧率为 120expected: 60, // 期望帧率为 60}}, () => { // 动画更新逻辑this.rotateAngle = 90 // 修改旋转角度为 90 度})}).onClick(() => { // 点击按钮时触发以下逻辑this.uiContext?.animateTo({ duration: 0 }, () => { // 停止旋转动画this.rotateAngle = 0 // 将旋转角度重置为 0 度})})}.width('100%').margin({ top: 5 }) // 设置列容器宽度为 100%,顶部外边距为 5}
}
以下是对于 组件代码解释,以及 UIContext
的作用和使用场景:
组件概述
AnimateToExample
是一个标记为入口组件 (@Entry
) 和可复用组件 (@Component
) 的结构体,用于展示按钮动画效果。它通过 UIContext
提供的动画 API 实现了按钮大小变化和旋转动画的功能。
关键变量
uiContext: UIContext | undefined = undefined;
- 定义了一个变量
uiContext
,用于存储当前的 UI 上下文。 UIContext
是 ArkTS 中的一个重要对象,提供了对动画、生命周期等操作的支持。
- 定义了一个变量
生命周期方法
aboutToAppear()
-
在组件即将显示时调用。
-
UIContext
的获取:this.uiContext = this.getUIContext();
- 调用
getUIContext()
方法获取当前的 UI 上下文,并赋值给uiContext
。 - 如果未成功获取 UI 上下文,则打印警告信息
"no uiContext"
并返回。
- 调用
UI 结构 (build()
方法)
垂直布局容器 (Column()
)
- 创建一个垂直布局容器,包含两个按钮,并设置其宽度为
100%
和顶部外边距为 5。
按钮 1:change size
-
功能:点击按钮时,按钮的宽度和高度会在两种状态之间切换。
-
UIContext
的使用:this.uiContext?.animateTo({duration: 2000,curve: Curve.EaseOut,iterations: 1,playMode: PlayMode.Normal,onFinish: () => {console.info('play end');} }, () => {this.widthSize = 150;this.heightSize = 60; });
- 调用
uiContext.animateTo
方法执行动画:duration: 2000
:动画持续时间为 2000 毫秒。curve: Curve.EaseOut
:动画曲线为EaseOut
。iterations: 1
:动画循环次数为 1 次。playMode: PlayMode.Normal
:动画播放模式为Normal
。onFinish
:动画结束时回调函数打印日志"play end"
。
- 动画更新逻辑中,修改按钮的宽度为
150
,高度为60
。
- 调用
按钮 2:stop rotating
-
功能:点击按钮时,停止旋转动画并将旋转角度重置为 0。
-
UIContext
的使用:this.uiContext?.animateTo({duration: 1200,curve: Curve.Friction,delay: 500,iterations: -1,playMode: PlayMode.Alternate,expectedFrameRateRange: {min: 10,max: 120,expected: 60,} }, () => {this.rotateAngle = 90; });
-
在组件出现时(
onAppear
方法),调用uiContext.animateTo
方法启动无限循环的旋转动画:duration: 1200
:动画持续时间为 1200 毫秒。curve: Curve.Friction
:动画曲线为Friction
。delay: 500
:动画延迟 500 毫秒后开始。iterations: -1
:动画无限循环(-1
表示无限)。playMode: PlayMode.Alternate
:动画播放模式为Alternate
(来回交替)。expectedFrameRateRange
:设置期望的帧率范围为min: 10
,max: 120
,expected: 60
。- 动画更新逻辑中,修改旋转角度为
90
度。
-
点击按钮时,调用
uiContext.animateTo
方法停止旋转动画:this.uiContext?.animateTo({ duration: 0 }, () => {this.rotateAngle = 0; });
- 设置动画持续时间为 0。
- 将旋转角度重置为
0
度。
-
总结
在该组件中,UIContext
的主要作用是提供动画相关的功能支持,具体包括:
- 获取上下文:通过
getUIContext()
方法获取当前的 UI 上下文。 - 执行动画:通过
animateTo
方法实现平滑的动画效果,支持多种参数配置(如duration
、curve
、iterations
等)。 - 动态更新状态:在动画更新逻辑中,动态修改状态变量(如
widthSize
、heightSize
和rotateAngle
),从而实现按钮大小变化和旋转动画的效果。
通过 UIContext
的灵活使用,该组件展示了 ArkTS 中动画功能的强大能力。