本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

   在ArkTS卡片开发中,页面交互是最核心的功能之一。用户点击卡片上的按钮,可以跳转到应用内的指定页面、传递消息给应用、甚至拉起后台服务。

  • 三种事件类型:router、message、call

  • 两种卡片形态:动态卡片(postCardAction)和静态卡片(FormLink)

一、卡片交互

ArkTS卡片提供三种页面交互能力:

交互能力 说明
页面跳转 卡片跳转到卡片提供方应用的指定UIAbility
拉起进程 卡片拉起卡片提供方进程
消息传递 卡片与卡片提供方之间的消息通信

卡片形态与对应接口

卡片类型 交互接口 适用场景
动态卡片 postCardAction 需要动态交互、数据更新的卡片
静态卡片 FormLink 简单展示、无需动态更新的卡片

二、三种事件类型

postCardActionFormLink均支持以下三种事件类型:

事件类型 作用 典型场景
router事件 跳转到指定UIAbility 点击卡片进入应用详情页
call事件 拉起UIAbility到后台 音乐播放、后台任务
message事件 触发onFormEvent回调 数据刷新、状态同步

2.1 router事件(页面跳转)

作用:跳转到指定UIAbility(页面)

限制

  • 非系统应用仅支持跳转到自己应用内的UIAbility

  • 不能跳转到其他应用的页面

典型应用

  • 相机卡片:点击"拍照"按钮进入相机界面

  • 天气卡片:点击"详情"进入天气预报页面

  • 待办卡片:点击"查看全部"进入待办列表

2.2 call事件(后台拉起)

作用:拉起指定UIAbility到后台

特点

  • UIAbility在后台运行

  • 可通过UIAbility申请后台长时任务

  • 适合音乐播放、位置跟踪等场景

2.3 message事件(消息传递)

作用:拉起FormExtensionAbility,触发onFormEvent回调

特点

  • 卡片发送消息给应用

  • 应用在onFormEvent中处理消息

  • 适合数据刷新、状态同步等场景

三、示例:卡片跳转到应用页面(router事件)

示例功能:

  • 卡片上有两个按钮:"FunA页面"和"FunB页面"

  • 点击不同按钮,跳转到应用内不同的页面

  • 通过params传递参数,区分跳转目标

3.1 创建卡片布局

// src/main/ets/widgeteventrouter/pages/WidgetEventRouterCard.ets

@Entry
@Component
struct WidgetEventRouterCard {
    build() {
        Column() {
            // 标题文字
            Text($r('app.string.JumpLabel'))
                .fontColor('#FFFFFF')
                .opacity(0.9)
                .fontSize(14)
                .margin({ top: '8%', left: '10%' })

            // 按钮区域
            Row() {
                Column() {
                    // 按钮A:跳转到FunA页面
                    Button() {
                        Text($r('app.string.ButtonA_label'))
                            .fontColor('#45A6F4')
                            .fontSize(12)
                    }
                    .width(120)
                    .height(32)
                    .margin({ top: '20%' })
                    .backgroundColor('#FFFFFF')
                    .borderRadius(16)
                    .onClick(() => {
                        // 发送router事件,携带targetPage=funA
                        postCardAction(this, {
                            action: 'router',
                            abilityName: 'EntryAbility',  // 目标UIAbility
                            params: { targetPage: 'funA' } // 自定义参数
                        });
                    })

                    // 按钮B:跳转到FunB页面
                    Button() {
                        Text($r('app.string.ButtonB_label'))
                            .fontColor('#45A6F4')
                            .fontSize(12)
                    }
                    .width(120)
                    .height(32)
                    .margin({ top: '8%', bottom: '15vp' })
                    .backgroundColor('#FFFFFF')
                    .borderRadius(16)
                    .onClick(() => {
                        // 发送router事件,携带targetPage=funB
                        postCardAction(this, {
                            action: 'router',
                            abilityName: 'EntryAbility',
                            params: { targetPage: 'funB' }
                        });
                    })
                }
            }
            .width('100%')
            .height('80%')
            .justifyContent(FlexAlign.Center)
        }
        .width('100%')
        .height('100%')
        .alignItems(HorizontalAlign.Start)
        .backgroundImage($r('app.media.CardEvent'))
        .backgroundImageSize(ImageSize.Cover)
    }
}

说明

字段 说明
action: 'router' 指定事件类型为router
abilityName: 'EntryAbility' 指定要跳转的UIAbility
params 传递自定义参数,用于区分不同按钮

3.2 在UIAbility中处理router事件

// src/main/ets/entryability/EntryAbility.ts

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = 'EntryAbility';
const DOMAIN_NUMBER: number = 0xFF00;

export default class EntryAbility extends UIAbility {
    private selectPage: string = 'funA';  // 默认页面
    private currentWindowStage: window.WindowStage | null = null;

    // 1. 首次创建时触发
    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        hilog.info(DOMAIN_NUMBER, TAG, `Ability onCreate, ${JSON.stringify(want)}`);
        
        // 解析卡片传递的参数
        if (want?.parameters?.params) {
            // want.parameters.params 对应 postCardAction() 中的 params
            let params: Record<string, Object> = JSON.parse(want.parameters.params as string);
            this.selectPage = params.targetPage as string;
            hilog.info(DOMAIN_NUMBER, TAG, `onCreate selectPage: ${this.selectPage}`);
        }
    }

    // 2. 如果UIAbility已在后台运行,收到router事件后触发
    onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        hilog.info(DOMAIN_NUMBER, TAG, `onNewWant Want: ${JSON.stringify(want)}`);
        
        // 解析卡片传递的参数
        if (want?.parameters?.params) {
            let params: Record<string, Object> = JSON.parse(want.parameters.params as string);
            this.selectPage = params.targetPage as string;
            hilog.info(DOMAIN_NUMBER, TAG, `onNewWant selectPage: ${this.selectPage}`);
        }

        // 重新加载页面
        if (this.currentWindowStage !== null) {
            this.onWindowStageCreate(this.currentWindowStage);
        }
    }

    // 3. 窗口创建时加载对应页面
    onWindowStageCreate(windowStage: window.WindowStage): void {
        let targetPage: string;

        // 根据selectPage的值,选择不同的页面
        switch (this.selectPage) {
            case 'funA':
                targetPage = 'funpages/FunA';
                break;
            case 'funB':
                targetPage = 'funpages/FunB';
                break;
            default:
                targetPage = 'pages/Index';
        }

        // 保存WindowStage实例
        if (this.currentWindowStage === null) {
            this.currentWindowStage = windowStage;
        }

        // 加载目标页面
        windowStage.loadContent(targetPage, (err, data) => {
            if (err.code) {
                hilog.error(DOMAIN_NUMBER, TAG, 'Failed to load the content. Cause: %{public}s', 
                    JSON.stringify(err) ?? '');
                return;
            }
            hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in loading the content. Data: %{public}s', 
                JSON.stringify(data) ?? '');
        });
    }
}

生命周期说明

回调 触发时机
onCreate UIAbility首次创建时触发
onNewWant UIAbility已在后台运行,收到新请求时触发
onWindowStageCreate 窗口创建时触发,用于加载页面

3.3 创建目标页面

FunA.ets
// src/main/ets/funpages/FunA.ets

@Entry
@Component
struct FunA {
    build() {
        Column() {
            // 标题栏
            Row() {
                Text($r('app.string.ButtonA_label'))
                    .fontSize(24)
                    .fontWeight(FontWeight.Bold)
                    .textAlign(TextAlign.Start)
                    .margin({
                        top: 12,
                        bottom: 11,
                        right: 24,
                        left: 24
                    })
            }
            .width('100%')
            .height(56)
            .justifyContent(FlexAlign.Start)

            // 空状态图片
            Image($r('app.media.pic_empty'))
                .width(120)
                .height(120)
                .margin({ top: 224 })

            // 空状态文字
            Text($r('app.string.NoContentAvailable'))
                .fontSize(14)
                .fontColor($r('app.color.text_color'))
                .opacity(0.4)
                .margin({
                    top: 8,
                    bottom: 317,
                    right: 152,
                    left: 152
                })
        }
        .width('100%')
        .height('100%')
    }
}
FunB.ets
// src/main/ets/funpages/FunB.ets

@Entry
@Component
struct FunB {
    build() {
        Column() {
            // 标题栏
            Row() {
                Text($r('app.string.ButtonB_label'))
                    .fontSize(24)
                    .fontWeight(FontWeight.Bold)
                    .textAlign(TextAlign.Start)
                    .margin({
                        top: 12,
                        bottom: 11,
                        right: 24,
                        left: 24
                    })
            }
            .width('100%')
            .height(56)
            .justifyContent(FlexAlign.Start)

            // 空状态图片
            Image($r('app.media.pic_empty'))
                .width(120)
                .height(120)
                .margin({ top: 224 })

            // 空状态文字
            Text($r('app.string.NoContentAvailable'))
                .fontSize(14)
                .fontColor($r('app.color.text_color'))
                .opacity(0.4)
                .margin({
                    top: 8,
                    bottom: 317,
                    right: 152,
                    left: 152
                })
        }
        .width('100%')
        .height('100%')
    }
}

3.4 配置页面路由

main_pages.json中配置所有页面:

// src/main/resources/base/profile/main_pages.json
{
  "src": [
    "pages/Index",           // 默认主页
    "funpages/FunA",          // FunA页面
    "funpages/FunB"           // FunB页面
  ]
}

3.5 配置资源文件

// src/main/resources/zh_CN/element/string.json
{
  "string": [
    {
      "name": "ButtonA_label",
      "value": "FunA页面"
    },
    {
      "name": "ButtonB_label",
      "value": "FunB页面"
    },
    {
      "name": "JumpLabel",
      "value": "router事件跳转"
    },
    {
      "name": "NoContentAvailable",
      "value": "暂无内容"
    }
  ]
}

3.6 运行效果

场景 操作 结果
卡片初始状态 - 显示两个按钮:"FunA页面"和"FunB页面"
点击"FunA页面" 卡片发送router事件 应用拉起,跳转到FunA页面
点击"FunB页面" 卡片发送router事件 应用拉起,跳转到FunB页面

四、三种事件对比

4.1 事件类型对比表

对比项 router事件 call事件 message事件
目标 UIAbility(前台) UIAbility(后台) FormExtensionAbility
拉起方式 前台显示 后台运行 后台运行
典型场景 页面跳转 音乐播放 数据刷新
生命周期 onCreate/onNewWant onCreate/onNewWant onFormEvent

4.2 使用示例对比

// router事件 - 跳转到应用页面
postCardAction(this, {
    action: 'router',
    abilityName: 'EntryAbility',
    params: { page: 'detail' }
});

// call事件 - 后台播放音乐
postCardAction(this, {
    action: 'call',
    abilityName: 'PlayAbility',
    params: { song: 'xxx.mp3' }
});

// message事件 - 刷新卡片数据
postCardAction(this, {
    action: 'message',
    params: { refresh: true }
});

五、注意事项

5.1 限制

限制项 说明
跨应用跳转 非系统应用只能跳转到自己应用内的UIAbility
参数传递 params参数需要通过JSON.parse解析
静态卡片 使用FormLink替代postCardAction

5.2 参数解析

// 卡片发送时
postCardAction(this, {
    action: 'router',
    abilityName: 'EntryAbility',
    params: { targetPage: 'funA' }
});

// UIAbility接收时
// want.parameters.params 是JSON字符串,需要解析
let params: Record<string, Object> = JSON.parse(want.parameters.params as string);
let targetPage = params.targetPage; // 获取到 'funA'

5.3 生命周期处理

// 两种情况需要处理:
// 1. 首次启动 - onCreate
// 2. 已存在实例 - onNewWant

onCreate(want) {
    // 处理首次启动
}

onNewWant(want) {
    // 处理后台唤醒
    // 需要重新加载页面
}

总结

ArkTS卡片:

  1. 三种事件类型

    • router:前台页面跳转

    • call:后台服务拉起

    • message:消息传递

  2. 两种卡片形态

    • 动态卡片:postCardAction

    • 静态卡片:FormLink

  3. router事件完整流程

    • 卡片:postCardAction({ action: 'router', abilityName, params })

    • UIAbility:onCreate/onNewWant解析params

    • 根据params加载不同页面

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐