前言
在鸿蒙应用开发过程中,我们经常会遇到需要获取UI上下文实例或者在非UI上下文中调用UI相关方法的场景。随着HarmonyOS NEXT的不断发展,UIContext API为我们提供了更加优雅的解决方案。本文将详细介绍如何使用UIContext中对应的接口获取与实例绑定的对象,以及如何以全屏方式拉起元服务。
一、UIContext概述
1.1 什么是UIContext?
在HarmonyOS NEXT的Stage模型中,WindowStage/Window通过loadContent接口加载页面并创建UI实例,将页面内容渲染到关联的窗口中。每个UI实例都与特定窗口一一关联。UIContext提供了与特定UI实例关联的执行上下文,确保UI操作能够在正确的上下文中执行。
1.2 为什么需要UIContext?
一些全局的UI接口需要依赖具体的UI执行上下文。在非UI页面(如UIAbility)或异步回调中调用这类接口时,系统可能无法跟踪到当前UI的上下文,导致接口执行失败。UIContext解决了这个问题,它让我们能够明确指定UI操作的执行上下文。
二、获取UIContext实例的方法
2.1 在组件内获取UIContext
在有@Component装饰器的组件中,可以直接使用this.getUIContext()方法获取UIContext实例:
typescript
@Component
struct MyComponent {
private uiContext: UIContext = this.getUIContext();
build() {
// 组件内容
}
}
2.2 全局获取和使用UIContext
在EntryAbility.ts的onWindowStageCreate方法中获取UIContext实例并存储到AppStorage中,方便全局使用:
typescript
// EntryAbility.ts
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
// 获取UIContext并存储到AppStorage
let context = windowStage.getMainWindowSync().getUIContext();
AppStorage.setOrCreate('UIContext', context);
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
// 在其他类或组件中使用
const uiContext: UIContext = AppStorage.get('UIContext') as UIContext;
2.3 通过window获取UIContext
从API version 10开始,可以使用ohos.window中的getUIContext()方法获取UIContext实例:
typescript
import window from '@ohos.window'; // 获取当前窗口的UIContext let windowInstance: window.Window = // 获取window实例 let uiContext: UIContext = windowInstance.getUIContext();
三、UIContext的核心接口及使用示例
3.1 获取与实例绑定的对象
UIContext提供了多种方法获取与特定UI实例绑定的对象:
获取Font对象
typescript
let font: Font = uiContext.getFont();
获取MediaQuery对象
typescript
let mediaQuery: MediaQuery = uiContext.getMediaQuery();
获取Router对象
typescript
let router: Router = uiContext.getRouter();
获取PromptAction对象
typescript
let promptAction: PromptAction = uiContext.getPromptAction();
获取ComponentUtils对象
typescript
let componentUtils: ComponentUtils = uiContext.getComponentUtils();
获取UIInspector对象
typescript
let uiInspector: UIInspector = uiContext.getUIInspector();
3.2 使用animateTo创建动画
animateTo接口可以指定由于闭包代码导致的状态变化插入过渡动效:
typescript
// xxx.ets
@Entry
@Component
struct AnimateToExample {
@State widthSize: number = 250;
@State heightSize: number = 100;
@State rotateAngle: number = 0;
private flag: boolean = true;
build() {
Column() {
Button('change size')
.width(this.widthSize)
.height(this.heightSize)
.margin(30)
.onClick(() => {
if (this.flag) {
uiContext.animateTo({
duration: 2000,
curve: Curve.EaseOut,
iterations: 3,
playMode: PlayMode.Normal,
onFinish: () => {
console.info('play end');
}
}, () => {
this.widthSize = 150;
this.heightSize = 60;
});
} else {
uiContext.animateTo({}, () => {
this.widthSize = 250;
this.heightSize = 100;
});
}
this.flag = !this.flag;
});
}.width('100%').margin({ top: 5 });
}
}
3.3 使用showAlertDialog显示警告弹窗
typescript
uiContext.showAlertDialog(
{
title: 'title',
message: 'text',
autoCancel: true,
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 },
gridCount: 3,
confirm: {
value: 'button',
action: () => {
console.info('Button-clicking callback');
}
},
cancel: () => {
console.info('Closed callbacks');
}
}
);
3.4 使用showActionSheet显示列表弹窗
typescript
uiContext.showActionSheet(
{
title: '标题',
message: '内容',
autoCancel: true,
confirm: {
value: '确认',
action: () => {
console.info('确认按钮点击回调');
}
},
// 其他配置...
}
);
四、全屏方式拉起元服务的方法
4.1 鸿蒙中常见的拉起方式对比
在HarmonyOS中,有多种方式可以拉起应用或元服务,下表对比了主要的几种方式:
| 拉起方式 | 描述 | 典型用途 | 适用场景 | 参数要求 | 特点与限制 |
|---|---|---|---|---|---|
| openLink | 使用URL Scheme唤起目标应用 | 三方跳转、H5打开App | 适用于已注册URI的目标服务 | 需注册scheme,例如myapp://page?param=x | 通用性强,依赖目标注册URI,不能确保一定成功 |
| startAbility | 显式或隐式Want启动指定UIAbility | 跨应用模块调用、能力联动 | 应用内跳转、已知bundleName的组件跳转 | 需指定bundleName、abilityName、action | 功能强大,多用于深度集成,适合系统内交互 |
| openAtomicService | 拉起免安装元服务(原子服务) | 快速使用外部工具/服务 | 一键唤起如扫码、识图、剪辑等元服务 | 设置action、serviceIdentity、appId等 | 支持免安装,需注册为原子服务,调用链受限 |
| FullScreenLaunchComponent | 以全屏方式嵌入式启动目标元服务 | 沉浸式嵌入服务 | 元服务间联动,如地图内嵌房产展示 | 需配置FullScreenLaunchComponent权限 | 仅元服务可用,适配复杂、须授权,鸿蒙6前存在兼容性问题 |
4.2 使用FullScreenLaunchComponent全屏拉起元服务
FullScreenLaunchComponent允许以全屏方式嵌入式启动元服务组件,当被拉起方授权使用方可以嵌入式运行元服务时,使用方可以全屏嵌入式运行元服务;未授权时,使用方跳出式拉起元服务。
基本使用
typescript
import { InnerFullScreenLaunchComponent, LaunchController } from '@kit.ArkUI';
@Entry
@Component
struct Index {
appId1: string = '5765880207853275505'; // 元服务appId
appId2: string = '5765880207854372375'; // 另一个元服务appId
@Builder
ColumnChild() {
Column() {
Text('InnerFullScreenLaunchComponent').fontSize(16).margin({top: 100});
Button('启动日出日落元服务')
.onClick(() => {
this.controller.launchAtomicService(this.appId2, {});
}).height(30).width('50%').margin({top: 50});
Button('启动充值元服务')
.onClick(() => {
let appId = '5765880207853275489';
this.controller.launchAtomicService(appId, {});
}).height(30).width('50%').margin({top: 50});
}.backgroundColor(Color.Pink).height('100%').width('100%');
}
controller: LaunchController = new LaunchController();
build() {
Column() {
InnerFullScreenLaunchComponent({
content: this.ColumnChild,
controller: this.controller,
});
}
.width('100%').height('100%');
}
}
实现原理
导入模块:首先需要导入
InnerFullScreenLaunchComponent和LaunchController。创建LaunchController:实例化一个拉起控制器,用于控制元服务的拉起行为。
构建界面内容:使用
@Builder装饰器构建要显示的内容,通常包含触发拉起操作的按钮。调用launchAtomicService方法:通过控制器的
launchAtomicService方法拉起指定的元服务,需要传入元服务的appId和可选参数。
注意事项
系统接口:
InnerFullScreenLaunchComponent是系统接口,从API Version 12开始支持。继承要求:如果要在元服务中实现嵌入式运行,必须继承自
EmbeddableUIAbility,否则系统无法保证元服务功能正常。权限配置:需要配置
FullScreenLaunchComponent权限。兼容性:在HarmonyOS 6之前存在兼容性问题,开发时需要注意。
4.3 使用openAtomicService拉起元服务
除了全屏嵌入式拉起,还可以使用openAtomicService方法直接拉起元服务:
typescript
openAtomicService(appId: string, parameters?: Record<string, Object>, context?: common.UIAbilityContext) {
const contextP = context ?? getContext() as common.UIAbilityContext;
const options: AtomicServiceOptions = {
displayId: 0,
parameters
};
contextP.openAtomicService(appId, options)
.then(() => {
console.log('openAtomicService success');
})
.catch((err: BusinessError) => {
console.error(`openAtomicService failed: ${err.code}, ${err.message}`);
});
}
// 示例调用
Button('拉起学习通元服务').onClick(() => {
this.openAtomicService('5765880207855627899');
});
五、实战案例:结合UIContext与全屏拉起元服务
下面是一个综合案例,演示如何在异步回调中使用UIContext执行UI操作,并在操作完成后拉起元服务:
typescript
@Entry
@Component
struct IntegratedExample {
@State private dialogVisible: boolean = false;
private uiContext: UIContext = this.getUIContext();
private controller: LaunchController = new LaunchController();
private targetAppId: string = '5765880207853275505';
// 显示对话框并设置超时后拉起元服务
showDialogAndLaunch() {
// 使用UIContext显示对话框
this.uiContext.showAlertDialog({
title: '确认拉起',
message: '是否要全屏拉起元服务?',
autoCancel: true,
confirm: {
value: '确认',
action: () => {
// 用户确认后直接拉起
this.launchAtomicService();
}
},
cancel: () => {
console.info('用户取消拉起操作');
}
});
// 设置超时,10秒后自动拉起
setTimeout(() => {
this.launchAtomicService();
}, 10000);
}
// 拉起元服务
launchAtomicService() {
try {
this.controller.launchAtomicService(this.targetAppId, {});
console.info('元服务拉起成功');
} catch (error) {
console.error(`元服务拉起失败: ${error.code}, ${error.message}`);
// 失败后使用UIContext显示错误信息
this.uiContext.showAlertDialog({
title: '拉起失败',
message: '元服务拉起失败,请重试或检查配置',
confirm: {
value: '确定',
action: () => {}
}
});
}
}
@Builder
MainContent() {
Column() {
Text('UIContext与元服务拉起演示')
.fontSize(20)
.margin({ bottom: 30 });
Button('点击显示对话框并拉起元服务')
.onClick(() => {
this.showDialogAndLaunch();
})
.width('80%')
.height(40);
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
build() {
Column() {
InnerFullScreenLaunchComponent({
content: this.MainContent,
controller: this.controller,
});
}
.width('100%')
.height('100%');
}
}
六、开发注意事项与最佳实践
上下文明确性:确保在UI上下文明确的地方使用UIContext方法,避免在UIAbility或异步回调中直接调用UI方法。
错误处理:在使用全屏拉起元服务时,始终添加错误处理逻辑,应对权限不足、元服务不存在等情况。
用户体验:在使用全屏嵌入式拉起时,提供清晰的用户界面和操作指引,让用户了解当前状态。
权限申请:确保在应用中申请必要的权限,并在尝试拉起前检查权限状态。
兼容性检查:在使用较新的API(如InnerFullScreenLaunchComponent)时,检查系统版本兼容性,必要时提供降级方案。
结语
UIContext是HarmonyOS NEXT中非常重要的API,它解决了UI上下文不明确导致的操作失败问题,为开发者提供了更灵活的UI控制能力。结合全屏拉起元服务的技术,可以创造出更加丰富和沉浸式的用户体验。希望通过本文的介绍,能够帮助大家更好地理解和应用这些技术,开发出更高质量的鸿蒙应用。