# UIExtensionComponent (系统接口)
UIExtensionComponent用于支持在本页面内嵌入其他应用提供的UI。展示的内容在另外一个进程中运行,本应用并不参与其中的布局和渲染。
通常用于有进程隔离诉求的模块化开发场景。
> **说明:**
>
> 该组件从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
>
> 本模块为系统接口。
## 使用约束
本组件不支持预览。
被拉起的Ability必须是带UI的Ability扩展,如何实现带UI的Ability扩展请参考[实现带UI的Ability扩展](../../apis-ability-kit/js-apis-app-ability-uiExtensionAbility.md)。
必须显式设置组件宽高为非0有效值。
不支持滚动到边界后,传递至上层继续滚动的场景。当UIExtensionComponent组件使用方和扩展Ability都支持内容滚动时,通过手势滚动会导致UIExtensionComponent内外同时响应,包括但不限于[Scroll](ts-container-scroll.md)、[Swiper](ts-container-swiper.md)、[List](ts-container-list.md)、[Grid](ts-container-grid.md)等滚动容器。内外手势同时滚动场景的规避方法可参考[示例2](#示例2-uec内外部同时响应滚动时隔离处理)。
## 子组件
无
## 接口
UIExtensionComponent(want: Want, options?: UIExtensionOptions)
**参数:**
| 参数名 | 参数类型 | 必填 | 参数描述 |
| --------------------- | ---------------------------------------------------------- | ---- | ------------------ |
| want | [Want](../../apis-ability-kit/js-apis-app-ability-want.md) | 是 | 要加载的Ability。 |
| options11+ | [UIExtensionOptions](#uiextensionoptions11) | 否 | 需要传递的构造项。 |
## 属性
支持[通用属性](ts-universal-attributes-size.md)。
## 事件
不支持[点击](ts-universal-events-click.md)等通用事件。
将事件经过坐标转换后传递给对端Ability处理。
支持以下事件:
### onRemoteReady
onRemoteReady(callback: [Callback](../../apis-basic-services-kit/js-apis-base.md#callback)\)
UIExtensionAbility连接完成时的回调,之后可使用proxy向被拉起的Ability发送数据。
**参数:**
| 参数名 | 类型 | 说明 |
| ---------------------------- | ------ | ------------------------------------------------------------ |
| proxy | UIExtensionProxy | 用于向对端Ability发送数据。 |
### onReceive
onReceive(callback: [Callback](../../apis-basic-services-kit/js-apis-base.md#callback)\<{ [key: string]: Object }>)
收到被拉起的Ability发送的数据时触发的回调。
**参数:**
| 参数名 | 类型 | 说明 |
| ---------------------------- | ------ | ------------------------------------------------------------ |
| data | { [key: string]: Object } | 收到来自对端Ability的数据。 |
### onResult(deprecated)
onResult(callback: [Callback](../../apis-basic-services-kit/js-apis-base.md#callback)\<{code: number; want?: Want}>)
被拉起的Ability扩展调用terminateSelfWithResult时会先触发本回调函数,再触发OnRelease。
本回调内可处理对端Ability的结果数据,可参考[AbilityResult](../../apis-ability-kit/js-apis-inner-ability-abilityResult.md)。
> **说明:**
> 从 API version 10 开始支持,从 API version 12 开始废弃,建议使用[onTerminated](#onterminated12)替代。
**参数:**
| 参数名 | 类型 | 说明 |
| ---------------------------- | ------ | ------------------------------------------------------------ |
| code | number | 收到来自对端Ability的处理結果code。 |
| want | Want | 收到来自对端Ability的处理結果[Want](../../apis-ability-kit/js-apis-app-ability-want.md)。 |
### onRelease(deprecated)
onRelease(callback: [Callback](../../apis-basic-services-kit/js-apis-base.md#callback)\)
用于处理被拉起的Ability销毁时的回调。
被拉起的Ability扩展调用terminateSelfWithResult或者terminateSelf时会触发本回调,此时releaseCode为0,即正常销毁。
被拉起的Ability扩展意外Crash或被kill时,触发本回调,此时releaseCode为1。
> **说明:**
> 从 API version 10 开始支持,从 API version 12 开始废弃,建议使用[onTerminated](#onterminated12)或者[onError](#onerror)替代。
**参数:**
| 参数名 | 类型 | 说明 |
| ---------------------------- | ------ | ------------------------------------------------------------ |
| releaseCode | number | 对端Ability销毁时的code,0为正常销毁,1为异常销毁。 |
### onError
onError(callback:[ErrorCallback](../../apis-basic-services-kit/js-apis-base.md#errorcallback))
被拉起的Ability扩展在运行过程中发生异常时触发本回调。可通过回调参数中的code、name和message获取错误信息并做处理。
**参数:**
| 参数名 | 类型 | 说明 |
| ---------------------------- | ------ | ------------------------------------------------------------ |
| err | [BusinessError](../../apis-basic-services-kit/js-apis-base.md#businesserror) | 报错信息。 |
### onTerminated12+
onTerminated(callback: Callback<TerminationInfo>)
被拉起的UIExtensionAbility通过调用`terminateSelfWithResult`或者`terminateSelf`正常退出时,触发本回调函数。
**参数:**
| 参数名 | 类型 | 说明 |
| ------- | ------ | ---------------------------------------------------------------------------------------- |
| callback | [Callback](../../apis-basic-services-kit/js-apis-base.md#callback)\<[TerminationInfo](#terminationinfo12)> | 回调函数,入参用于接收UIExtensionAbility的返回结果,类型为[TerminationInfo](#terminationinfo12)。 |
> **说明:**
>
> - 若UIExtensionAbility通过调用`terminateSelfWithResult`退出,其携带的信息会传给回调函数的入参;
> - 若UIExtensionAbility通过调用`terminateSelf`退出,上述回调函数的入参中,"code"取默认值"0","want"为"undefined"。
### TerminationInfo12+
用于表示被拉起的UIExtensionAbility通过调用`terminateSelfWithResult`或者`terminateSelf`正常退出时的返回结果。
| 属性名 | 类型 | 说明 |
| ------- | ------ | --------------------------------------------------- |
| code | number | 被拉起UIExtensionAbility退出时返回的结果码。 |
| want | [Want](../../apis-ability-kit/js-apis-app-ability-want.md) | 被拉起UIExtensionAbility退出时返回的数据。 |
## UIExtensionOptions11+
用于在UIExtensionComponent进行构造的时传递可选的构造参数。
**参数:**
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ---- | ---------------------------------------- | ---- | --------------- |
| isTransferringCaller | boolean | 否 | 在使用UIExtensionComponent嵌套时,设置当前UIExtensionComponent是否转发上一级的Caller信息。 默认值:false。 |
| placeholder12+ | [ComponentContent](../js-apis-arkui-ComponentContent.md) | 否 | 设置占位符,在UIExtensionComponent与UIExtensionAbility建立连接前显示。 |
| dpiFollowStrategy12+ | [DpiFollowStrategy](ts-container-ui-extension-component-sys.md#dpifollowstrategy12) | 否 | 提供接口支持设置DPI跟随宿主或跟随UIExtensionAbility。 默认值:FOLLOW_UI_EXTENSION_ABILITY_DPI。 |
| areaChangePlaceholder14+ | Record | 否 | 设置尺寸变化占位符,在UIExtensionComponent尺寸发生变化并且UIExtension内部渲染未完成时显示, key值支持"FOLD_TO_EXPAND"(折叠展开尺寸变化)、"UNDEFINED"(默认尺寸变化)。 |
## DpiFollowStrategy12+
| 名称 | 描述 |
| -------------------------------- | --------------- |
| FOLLOW_HOST_DPI | 表示DPI跟随宿主。 |
| FOLLOW_UI_EXTENSION_ABILITY_DPI | 表示DPI跟随UIExtensionAbility。 |
## UIExtensionProxy
用于在双方建立连接成功后,组件使用方向被拉起的Ability发送数据、订阅和取消订阅注册。
### send
send(data: { [key: string]: Object }): void
用于在双方建立连接成功后,组件使用方向被拉起的Ability发送数据的场景,提供异步发送数据。
**系统能力:** SystemCapability.ArkUI.ArkUI.Full
**参数:**
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ---- | ---------------------------------------- | ---- | --------------- |
| data | { [key: string]: Object }): { [key: string]: Object } |
### sendSync11+
sendSync(data: Record\): Record\
用于在双方建立连接成功后,组件使用方向被拉起的Ability发送数据的场景,提供同步发送数据。
**系统能力:** SystemCapability.ArkUI.ArkUI.Full
**参数:**
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ---- | ---------------------------------------- | ---- | --------------- |
| data | { [key: string]: Object } | 是 | 同步发送给被拉起的扩展Ability的数据。 |
**返回值:**
| 类型 | 描述 |
| ---- | ----|
| { [key: string]: Object } | 扩展Ability回复的数据。 |
**错误码:**
| 错误号 | 描述 |
| ---- | ----|
| 100011 | 扩展Ability未注册同步回调 |
| 100012 | 数据发送失败 |
### on('asyncReceiverRegister')11+
on(type: 'asyncReceiverRegister', callback: (proxy: UIExtensionProxy) => void): void
用于在双方建立连接成功后,组件使用方订阅被拉起的Ability发生异步注册的场景。
**系统能力:** SystemCapability.ArkUI.ArkUI.Full
**参数:**
| 参数名 | 参数类型 |必填 | 参数描述 |
| ------ | -------- |---- | ------- |
| type | string | 是 | 代表订阅扩展Ability发生异步注册回调。 |
| callback | (proxy: UIExtensionProxy) => void | 是 | 订阅扩展Ability注册setReceiveDataCallback后触发的回调。 |
### on('syncReceiverRegister')11+
on(type: 'syncReceiverRegister', callback: (proxy: UIExtensionProxy) => void): void
用于在双方建立连接成功后,组件使用方订阅被拉起的Ability发生同步注册的场景。
**系统能力:** SystemCapability.ArkUI.ArkUI.Full
**参数:**
| 参数名 | 参数类型 |必填 | 参数描述 |
| ------ | -------- |---- | ------- |
| type | string | 是 | 订阅扩展Ability发生同步注册回调。 |
| callback | (proxy: UIExtensionProxy) => void | 是 | 扩展Ability注册setReceiveDataForResultCallback后触发的回调。 |
### off('asyncReceiverRegister')11+
off(type: 'syncReceiverRegister', callback?: (proxy: UIExtensionProxy) => void): void
用于在双方建立连接成功后,组件使用方取消订阅被拉起的Ability发生异步注册的场景。
**系统能力:** SystemCapability.ArkUI.ArkUI.Full
**参数:**
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ------ | -------- | ----- | ------- |
| type | string | 是 | 取消订阅扩展Ability发生异步注册回调。 |
| callback | Callback\ | 否 | 为空代表取消订阅所有扩展Ability异步注册后触发回调。
非空代表取消订阅异步对应回调。 |
### off('syncReceiverRegister')11+
off(type: 'syncReceiverRegister', callback?: Callback\): void
用于在双方建立连接成功后,组件使用方取消订阅被拉起的Ability发生同步注册的场景。
**系统能力:** SystemCapability.ArkUI.ArkUI.Full
**参数:**
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ------ | -------- | ----- | ------- |
| type | string | 是 | 取消订阅扩展Ability发生同步注册回调。 |
| callback | (proxy: UIExtensionProxy) => void | 否 | 为空代表取消订阅所有扩展Ability同步注册后触发回调
非空代表取消订阅同步对应回调。 |
## 示例
### 示例1 (加载UIExtension)
UIExtensionComponent组件使用分为使用方和提供方。本示例仅展示组件使用的方法和扩展的Ability,实际运行需在设备中安装bundleName为"com.example.newdemo",abilityName为"UIExtensionProvider"的Ability扩展。
**组件使用方**
使用方入口界面Index.ets内容如下:
```ts
import { ComponentContent } from '@kit.ArkUI';
class Params {
}
@Builder
function LoadingBuilder(params: Params) {
Column() {
LoadingProgress()
.color(Color.Blue)
}
}
@Builder
function AreaChangePlaceholderBuilder(params: Params) {
Column() {
}
.width('100%')
.height('100%')
.backgroundColor(Color.Orange)
}
@Entry
@Component
struct Second {
@State message1: string = 'Hello World 1'
@State message2: string = 'Hello World 2'
@State message3: string = 'Hello World 3'
@State visible: Visibility = Visibility.Hidden
@State wid: number = 300
@State hei: number = 300
private proxy: UIExtensionProxy | null = null;
private initPlaceholder = new ComponentContent(this.getUIContext(), wrapBuilder(LoadingBuilder), new Params);
private areaChangePlaceholder = new ComponentContent(this.getUIContext(), wrapBuilder(AreaChangePlaceholderBuilder), new Params);
build() {
Row() {
Column() {
Text(this.message1).fontSize(30)
Text(this.message2).fontSize(30)
Text(this.message3).fontSize(30)
UIExtensionComponent({
bundleName : "com.example.newdemo",
abilityName: "UIExtensionProvider",
parameters: {
"ability.want.params.uiExtensionType": "sys/commonUI"
}},
{
placeholder: this.initPlaceholder,
areaChangePlaceholder: {
"FOLD_TO_EXPAND" : this.areaChangePlaceholder,
}
})
.width(this.wid)
.height(this.hei)
.border({width: 5, color: Color.Blue})
.onReceive((data) => {
console.info('Lee onReceive, for test')
this.message3 = JSON.stringify(data['data'])
})
.onTerminated((info) => {
console.info('onTerminated: code =' + info.code + ', want = ' + JSON.stringify(info.want));
})
.onRemoteReady((proxy) => {
console.info('onRemoteReady, for test')
this.proxy = proxy
this.proxy.on("syncReceiverRegister", syncRegisterCallback1);
this.proxy.on("asyncReceiverRegister", (proxy1) => {
console.info("on invoke for test, type is asyncReceiverRegister");
});
})
Button("点击向UIExtensionAbility发送数据").onClick(() => {
if (this.proxy != undefined) {
this.proxy.send({data: "你好1"})
try {
let re = this.proxy.sendSync({data: "你好2"})
console.info("for test, re=" + JSON.stringify(re));
} catch (err) {
console.error(`sendSync failed for test. errCode=${err.code}, msg=${err.message}`);
}
}
})
}
.width('100%')
}
.height('100%')
}
}
function syncRegisterCallback1(proxy: UIExtensionProxy) {
console.info("on invoke for test, syncRegisterCallback1, type is syncReceiverRegister");
}
function syncRegisterCallback2(proxy: UIExtensionProxy) {
console.info("on invoke for test, syncRegisterCallback2, type is syncReceiverRegister");
}
```
**组件提供方**
提供方包含三个文件需要修改
- 提供方新增扩展入口文件/src/main/ets/uiextensionability/UIExtensionProvider.ets
```ts
import { UIExtensionAbility, UIExtensionContentSession, Want } from '@kit.AbilityKit';
const TAG: string = '[UIExtAbility]'
export default class UIExtAbility extends UIExtensionAbility {
onCreate() {
console.log(TAG, `UIExtAbility onCreate`)
}
onForeground() {
console.log(TAG, `UIExtAbility onForeground`)
}
onBackground() {
console.log(TAG, `UIExtAbility onBackground`)
}
onDestroy() {
console.log(TAG, `UIExtAbility onDestroy`)
}
onSessionCreate(want: Want, session: UIExtensionContentSession) {
console.log(TAG, `UIExtAbility onSessionCreate, want: ${JSON.stringify(want)}`)
let param: Record = {
'session': session
};
let storage: LocalStorage = new LocalStorage(param);
session.loadContent('pages/extension', storage);
}
onSessionDestroy(session: UIExtensionContentSession) {
console.log(TAG, `UIExtAbility onSessionDestroy`)
}
}
```
- 提供方扩展Ability入口页面文件/src/main/ets/pages/extension.ets
```ts
import { UIExtensionContentSession } from '@kit.AbilityKit';
let storage = LocalStorage.getShared()
AppStorage.setOrCreate('message', 'UIExtensionAbility')
@Entry(storage)
@Component
struct Extension {
@StorageLink('message') storageLink: string = '';
private session: UIExtensionContentSession | undefined = storage.get('session');
pathStack: NavPathStack = new NavPathStack()
@Builder
PageMap(name: string) {
if (name === "hello") {
pageOneTmp()
}
}
onPageShow() {
if (this.session != undefined) {
this.session.setReceiveDataCallback((data)=> {
this.storageLink = JSON.stringify(data)
console.info("invoke for test, handle callback set by setReceiveDataCallback successfully");
})
this.session.setReceiveDataForResultCallback(func1)
}
}
build() {
Navigation(this.pathStack) {
Row() {
Column() {
Text(this.storageLink)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Button("点击向Component发送数据").onClick(()=>{
if (this.session != undefined) {
this.session.sendData({"data": 543321})
console.info('send 543321, for test')
}
})
Button("terminate").onClick(()=> {
if (this.session != undefined) {
this.session.terminateSelf();
}
storage.clear()
})
Button("terminate with result").onClick(()=>{
if (this.session != undefined) {
this.session.terminateSelfWithResult({
resultCode: 0,
want: {
bundleName: "myBundleName",
parameters: { "result": 123456 }
}
})
}
storage.clear()
})
Button("点击跳转").onClick(()=> {
this.pathStack.pushPath({ name: "hello"})
})
}
}
.height('100%')
}.navDestination(this.PageMap)
.mode(NavigationMode.Stack)
}
}
// pageOne
@Component
export struct pageOneTmp {
pathStack: NavPathStack = new NavPathStack()
build() {
NavDestination() {
Column() {
Text("Hello World")
}.width('100%').height('100%')
}.title("pageOne")
.onBackPressed(() => {
const popDestinationInfo = this.pathStack.pop() // 弹出路由栈栈顶元素
console.log('pop' + '返回值' + JSON.stringify(popDestinationInfo))
return true
})
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack
})
}
}
function func1(data: Record): Record {
let linkToMsg: SubscribedAbstractProperty = AppStorage.link('message');
linkToMsg.set(JSON.stringify(data))
console.info("invoke for test, handle callback set by setReceiveDataForResultCallback successfully");
return data;
}
```
- 提供方扩展Ability, module配置文件/src/main/module.json5添加对应配置
```json
{
"name": "UIExtensionProvider",
"srcEntry": "./ets/uiextensionability/UIExtensionProvider.ets",
"description": "1",
"label": "$string:EntryAbility_label",
"type": "sys/commonUI",
"exported": true,
}
```
### 示例2 (UEC内外部同时响应滚动时隔离处理)
本示例展示了当UIExtensionComponent组件使用方和扩展的Ability同时使用[Scroll](ts-container-scroll.md)容器的场景,通过对UIExtensionComponent设置手势拦截处理,实现当UIExtensionComponent内部滚动时,外部组件不响应滚动。
手势使用方式:
组件内部滚动:手指在组件内部进行滚动操作;
组件外部滚动:拖动外部滚动条进行滚动。
实际运行时需先在设备中安装bundleName为"com.example.newdemo",abilityName为"UIExtensionProvider"的Ability扩展。
提供方扩展入口文件UIExtensionProvider.ets与[示例1](#示例1-加载uiextension)扩展入口文件UIExtensionProvider.ets代码一致。
提供方扩展Ability, module配置文件与[示例1](#示例1-加载uiextension)扩展module配置文件module.json5代码一致。
- 使用方组件使用示例:
```ts
@Entry
@Component
struct Second {
@State message1: string = 'Hello World 1'
@State message2: string = 'Hello World 2'
@State message3: string = 'Hello World 3'
@State visible: Visibility = Visibility.Hidden
@State wid: number = 300
@State hei: number = 300
private scroller: Scroller = new Scroller();
private arr: number[] = [0, 1, 2, 3, 4, 5, 6]
build() {
Column() {
// 可滚动的容器组件
Scroll(this.scroller) {
Column() {
Text(this.message1).fontSize(30)
Text(this.message2).fontSize(30)
Text(this.message3).fontSize(30)
// 重复设置组件,构造滚动内容
ForEach(this.arr, (item: number) => {
UIExtensionComponent({
bundleName: "com.example.newdemo",
abilityName: "UIExtensionProvider",
parameters: {
"ability.want.params.uiExtensionType": "sys/commonUI"
}
})
.width(this.wid)
.height(this.hei)
// 设置手势拦截,UEC外部组件不响应滚动
.gesture(PanGesture().onActionStart(() => {
console.info('UIExtensionComponent PanGesture onAction')
}))
.border({ width: 5, color: Color.Blue })
.onReceive((data) => {
console.info('Lee onReceive, for test')
this.message3 = JSON.stringify(data['data'])
})
.onTerminated((info) => {
console.info('onTerminated: code =' + info.code + ', want = ' + JSON.stringify(info.want));
})
.onRemoteReady((proxy) => {
console.info('onRemoteReady, for test')
})
}, (item: string) => item)
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical) // 滚动方向纵向
.scrollBar(BarState.On) // 滚动条常驻显示
.scrollBarColor(Color.Gray) // 滚动条颜色
.scrollBarWidth(10) // 滚动条宽度
.friction(0.6)
.edgeEffect(EdgeEffect.None)
.onWillScroll((xOffset: number, yOffset: number, scrollState: ScrollState) => {
console.info(xOffset + ' ' + yOffset)
})
.onScrollEdge((side: Edge) => {
console.info('To the edge')
})
.onScrollStop(() => {
console.info('Scroll Stop')
})
}
.height('100%')
}
}
```
- 提供方扩展Ability入口页面文件extension.ets
```ts
@Entry
@Component
struct Extension {
@StorageLink('message') storageLink: string = '';
private scroller: Scroller = new Scroller();
private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8]
build() {
Column() {
// 可滚动的容器组件
Scroll(this.scroller) {
Column() {
Text('Test demo')
.fontSize(20)
.fontWeight(FontWeight.Bold)
// 重复设置组件,构造滚动内容
ForEach(this.arr, (item: number) => {
Text(item.toString())
.width('90%')
.height(150)
.backgroundColor(Color.Pink)
.borderRadius(15)
.fontSize(16)
.textAlign(TextAlign.Center)
.margin({ top: 10 })
}, (item: string) => item)
}
}
}
.height('100%')
}
}
```