首页 > 其他分享 > 鸿蒙入门开发教程:一文带你详解工具箱元服务的开发流程

鸿蒙入门开发教程:一文带你详解工具箱元服务的开发流程

时间:2023-08-17 12:02:51浏览次数:42  
标签:targetPage 鸿蒙 卡片 app 开发 params 工具箱 Constants 函数

鸿蒙入门开发教程:一文带你详解工具箱元服务的开发流程

一,基本概念

  • 元服务(原名原子化服务)是一种基于HarmonyOS API的全新服务提供方式,以HarmonyOS万能卡片等多种呈现形态,向用户提供更轻量化的服务。具有即用即走、信息外显、服务直达的特性。
  • 万能卡片(简称卡片)是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。
  • ArkUI框架是一套构建分布式应用界面的声明式UI开发框架,其使用极简的UI信息语法、丰富的UI组件、以及实时界面预览工具,帮助开发者提升HarmonyOS应用界面开发效率。
  • AppGallery Connect(简称AGC)致力于为应用的创意、开发、分发、运营、经营各环节提供一站式服务,构建全场景智慧化的应用生态体验。
  • 端云一体化是为丰富HarmonyOS对云端开发的支持、实现HarmonyOS生态端云联动,DevEco Studio推出了云开发功能,开发者在创建工程时选择云开发模板,即可在DevEco Studio内同时完成HarmonyOS应用/服务的端侧与云侧开发。

二,主要功能

本次开发的实用小工具主要功能有:

1.油价查询,点击城市列表之后,就可以查看当前城市的油价,包括92,95,98,柴油等。

2.垃圾分类查询,输入物品名称就可以查看垃圾的详细分类,而不再害怕垃圾分类出错。

3.当前定位查看,我们可以很方便的知道自己当前的位置信息。

4.提供12,22,24,44卡片。

API版本:API9

应用包名:com.jianguo.utilitybox

三,项目初始化

  • 当前未打开任何工程,单击DevEco Studio欢迎页中“Create Project”创建新工程。
  • 当前已打开工程,单击菜单栏“File > New > Create Project”创建新工程。

首先创建项目,选择原子化服务,第四个模版端云一体化模版

image-20230718122048111

点击下一步

image-20230718122233434

这个时候我们需要为工程关联云开发所需的资源,即在DevEco Studio中选择您的华为开发者账号加入的开发者团队,将该团队在AGC的同包名应用关联到当前工程。

image-20230718122314803

然后我们点击登陆就好

这个时候会来到下面的授权页面,我们点击允许就可以。

image-20230718122348179

单击“AppGallery Connect”打开AGC应用创建向导,填写应用信息,单击“确认”按钮创建应用

image-20230718122601491

我们点击箭头这儿就会跳转到我们的image-20230718122642644

agc页面。这个时候我们选择创建项目,然后再创建应用。我之前已经有项目了。所以我们直接在项目里创建应用就可以

比如我这里就选择

https://developer.huawei.com/consumer/cn/service/josp/agc/index.html#/myProject?appPackageName=com.jianguo.utilitybox&isAtomic=1

下面这个就可以

image-20230718122901052

然后就会跳转到设置位置处理页面

image-20230718122933972

我们点击下一步就会到下面的添加应用界面,然后我们只需要填写应用名称就可以,其他的都会默认,如果默认失败的话,我们就设置应用包名:com.jianguo.utilitybox

image-20230718123311208

然后确定

这个时候就会有恭喜您!

应用创建已完成。

image-20230718123445502

我们选择前往控制台,然后来到Severless这一块。把我们需要的认证服务,云函数,云数据库,云缓存,云存储,统统打开。

image-20230718124335651

我们回到IDE继续开发,点击Finsh就可。

image-20230718124214195

出现这个,我们ok就可以。

image-20230718124257821

然后大家可以看到工程配置完成

成功创建工程并关联云开发资源后,DevEco Studio会为工程自动执行一些初始化配置,并开通云开发相关服务:认证服务、云函数、云数据库、云托管、API网关、云存储。,所以上面我们在控制台的操作其实也可以不用操作。

image-20230718124855203

端侧工程自动集成AGC SDK,包括AGC SDK 配置文件entry/src/main/resources/rawfile/agconnect-services.json 和在entry/oh-package.json5配置文件中引入的AGC相关云服务最新版本SDK。

 "dependencies": {
    "@hw-agconnect/crypto-ohos": "^1.0.10",
    "@hw-agconnect/function-ohos": "^1.0.10",
    "@hw-agconnect/auth-ohos": "^1.0.10",
    "@hw-agconnect/cloudstorage-ohos": "^1.0.10",
    "@hw-agconnect/api-ohos": "^1.0.10",
    "@hw-agconnect/base-ohos": "^1.0.10",
    "@hw-agconnect/core-ohos": "^1.0.10",
    "@hw-agconnect/credential-ohos": "^1.0.10",
    "@ohos/agconnect-auth-component": "^1.0.5",
    "long": "5.2.1"
  }

云侧工程自动集成云数据库最新版本Node.js Server SDK。

/Users/jianguo/Desktop/teaching/utilitybox/CloudProgram/cloudfunctions/idgenerator/package.json

	"dependencies": {
		"@agconnect/database-server": "^1.0.7"
	}
}

四,端云一体化开发

大家都知道我这次用的是端云一体化开发,那那么我们就有必要来了解一下端云协同的目录结构

端云一体化开发工程目录分为三个子工程:如下图所示:

端开发工程(Application)、

云开发工程(CloudProgram)、

端侧公共库(External Libraries)。

image-20230718134729237

4.1端开发工程(Application)

端开发工程主要用于开发应用端侧的业务代码,端开发工程目录结构如下:

端侧的目录和之前大家看到的文件目录结构都差不多

- Application
    - AppScope
        app.json5    // 应用的全局配置信息
    - entry           // 应用/服务模块,编译构建生成一个HAP
        oh_modules   // 用于存放三方库依赖信息
        - src/main
            - ets    // 用于存放ArkTS源码
            - resources    // 用于存放应用/服务所用到的资源文件
            module.json5    // Stage模型配置文件
        build-profile.json5    // 当前模块信息、编译信息配置项
        hvigorfile.ts          // 模块级编译构建任务脚本
        oh-package.json5        // 配置三方包声明的入口及包名
    build-profile.json5    // 应用配置信息,包括签名、产品配置等
    hvigorfile.ts          // 应用级编译构建任务脚本  

4.2云开发工程(CloudProgram)

云开发工程中开发者可以为应用开发云函数和云数据库服务资源,云开发工程目录结构如下:

image-20230718140024688

- CloudProgram
    - clouddb    // 云数据库工程目录
        dataentry    // 用于存放数据条目文件
        objecttype    // 用于存放对象类型文件
        db-config.json    // 模块配置文件
    - cloudfunctions    // 云函数工程目录
        node_modules    // 包含所有三方依赖
        cloud-config.json    // 云开发工程配置文件
        package.json    // 定义了TypeScript公共依赖

五,云函数开发指南

5.1创建函数

在云端工程(CloudProgram)中可以创建函数、编写函数业务代码、为函数配置调用触发器。

1.单击“cloudfunctions”目录,选择“New > Cloud Function”创建云函数。

image-20230718141022604

2.输入函数名称,单击“OK”按钮DevEco Studio自动生成函数目录。函数名称仅支持小写英文字母、数字、中划线(-),首字母必须为小写字母,结尾不能为中划线(-)。

比如add。

3.云函数目录结构。

image-20230718141122389

- add
    node_modules    // 自动为该函数引入依赖包
    function-config.json    // 函数的配置文件,可配置触发器,通过触发器暴露的触发条件来实现函数调用。
    package.json            // 包含了当前函数的名称、版本等函数元数据。
    add.ts    // 函数入口文件

4.云函数触发器

云函数触发器在function-config.json文件中triggers属性中配置,当前支持HTTP触发器、CLOUDDB触发器、AUTH触发器、CLOUDSTORAGE触发器、CRON触发器五种。

  • HTTP触发器

    工程创建完成后默认自动在function-config.json文件生成HTTP触发器配置。函数部署到云端后会自动生成触发URL,开发者向URL发起HTTP请求时触发函数。

    {
      "handler": "IdGenerator.myHandler",
      "triggers": [
        {
          "type": "http",
          "properties": {
            "enableUrlDecode": true,//通过HTTP触发器触发函数,对于contentType为“application/x-www-form-urlencoded”的触发请求,是否使用URLDecoder对请求body进行解码再转发到函数中。true:启用。false:不启用。
            "authFlag": "true",//是否鉴权,默认为true。
            "authAlgor": "HDA-SYSTEM",//鉴权算法,默认为HDA-SYSTEM。
            "authType": "apigw-client",//HTTP触发器的认证类型。apigw-client:端侧网关认证,适用于来自APP客户端侧的函数调用。 cloudgw-client:云侧网关认证,适用于来自APP服务器侧的函数调用
            "mode": "NO_PATH"
          }
        }
      ]
    }
    

5.2开发云函数

云函数的代码实现基于不同的语言运行环境可分为Node.js、Java、Python,还有一种比较特别运行环境为Custom Runtime(自定义运行环境)。本工程的语言运行环境为Node.js。

1.云函数的入口方法:

module.exports.myHandler = function(event, context, callback, logger)

  • myHandler:入口方法名称。
  • event:调用方传递的事件对象,JSON格式。
  • context:函数运行时上下文对象,封装了日志接口、回调接口、环境变量env对象等。
  • callback:事件处理结果。
  • logger:记录日志。开发者在代码中使用logger接口记录日志,当前支持四种级别。
    • logger.debug()
    • logger.error()
    • logger.warn()
    • logger.info()

函数必须通过显示调用callback(object)将事件处理结果返回给AGC,结果可以是任意对象,但必须与JSON.stringify兼容,AGC会将结果转换成JSON字符串,返回给调用方。callback执行完成后,函数即执行结束。

2.为云函数添加返回内容

let myHandler = async function (event, context, callback, logger) {
  logger.info(event);

  // do something here

  callback({
    code: 0,
    desc: "Success."
  });
};

export { myHandler };

3.调试云函数 函数开发过程中,开发者可在本地进行调试,或者将函数部署到AGC云端后,在本地触发调用云端函数。当前本地调试支持Run和Debug两种模式,Debug模式支持使用断点来追踪函数的运行情况。

  • 本地云函数调试,单击"cloudfunctions > Run/Debug Cloud Function"运行/调试云函数

image-20230718142648579

查看Run面板,若出现“Cloud Functions loaded successfully”,标识云函数启动成功(云函数启动/调试将部署cloudfunctions中所有的云函数),并生成对应的POST URL。

image-20230718142725734

在菜单栏选择“Tools > CloudDev > Cloud Functions Requestor”触发云函数调用

image-20230718142836487

在弹出的云函数调用界面填写触发事件参数。

image-20230718142931972

  • Environment:选择函数调用环境,Local表示本地调用,Remote表示远程调用(需要先将函数部署到AGC云端)。
  • Cloud Function:选择需要触发的云函数。
  • Event:输入事件参数,内容为JSON格式请求体数据。

单击Trigger按钮,触发执行云函数,执行结果展示在Result框内,Run面板同时打印运行日志。

image-20230718143021007

image-20230718143206733

部署云函数 完成函数代码开发后,开发者可将函数部署到AGC控制台,支持单个部署和批量部署。

  • 右键单击需要部署的函数目录,选择“Deploy Cloud Functions”。

    image-20230718143454623

  • 底部状态栏右侧将显示函数打包与部署进度,直至出现“Deploy successfully”消息表示函数部署成功。

image-20230718143542547

登录AGC控制台,进入当前项目的云函数服务菜单,可查看开发者部署的函数。

image-20230718143711334

远程函数测试 在“Cloud Functions Reuestor”面板中,更改Environment为Remote远程调用,单击“Trigger”按钮,在Result中显示返回结果。

image-20230718143750867

当开发者创建的函数或函数别名中创建一个HTTP类型的触发器后,在应用客户端调用函数时需要传入HTTP触发器的标识,查询方法如下: 在函数的触发器页面点击“HTTPTrigger”触发器,查看“触发URL”的后缀,获取触发器标识,格式为“函数名-版本号”。

image-20230718144239539

5.3调用云函数

应用集成云函数SDK后,可以在应用内直接通过SDK API调用AGC中的云函数,云函数SDK与AGC的函数调用基于HTTPS的安全访问。

  • 在端侧应用(Application)中“entry > src/main/ets > services”目录创建SudokuAlgorithmFunction.ts文件,编写调用云函数方法。
Button('请求自定义云函数')
  .fontSize(16)
  .onClick(() => {
    getSudokuPuzzle(getContext(this)).then((ret) => {
      Log.info(Constants.LOG_TAG_NAME, `单击按钮调用云函数返回结果: ${JSON.stringify(ret)}`)
    })
  })

然后我们签名,在真机中

image-20230718150726723

六,华为认证服务

当前AGC认证服务为HarmonyOS应用/服务提供的登录认证方式有手机、邮箱和关联账号三种方式。本工程使用“邮箱+验证码”的方式作为应用的登录入口。我们这次使用的是手机号码登录

需要在AGC控制台开通认证服务(工程创建时默认开通),并在“认证方式”页签中启用“邮箱地址”。

image-20230722231345864

调用PhoneUserBuilder生成PhoneUser,然后调用PhoneAuthProvider.credentialWithVerifyCode注册用户。注册成功后,系统会自动登录,无需再次调用登录接口。也可以使用signIn登录接口,通过第三方认证来登录AGConnect平台,在entry/src/main/ets/services/Auth.ts认证工具类中添加邮箱账号注册用户方法。

    public async login(countryCode: string, phoneNumber: string, verifyCode: string): Promise<AgUser> {
        return new Promise((resolve, reject) => {
            const credential = PhoneAuthProvider.credentialWithVerifyCode(countryCode, phoneNumber, verifyCode);
            this.agc.auth().signIn(credential).then(async (ret) => {
                Log.info(TAG, "User has signed in..");
                // @ts-ignore
                let user = ret.getUser();
                let userExtra = await ret.getUser().getUserExtra();

                let loginRes = new AgUser(
                user.getUid(),
                user.getPhotoUrl(),
                user.getPhone(),
                user.getDisplayName(),
                userExtra.getCreateTime(),
                userExtra.getLastSignInTime())

                resolve(loginRes);
            }).catch((error) => {
                Log.error(TAG, "Error: ", error);
                reject(error);
            });
        });
    }

6.1构建手机登录页面

通过容器组件Flex、Row、Column以及基础组件Text、Image、Button、Navigation、TextInput,构建手机验证码登录页面。

    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Text($r('app.string.auth_dialog_title'))
        .fontSize($r('app.float.navigation_font_size'))
        .margin({ bottom: Constants.LENGTH_20_PX })

      Flex({ direction: FlexDirection.Row }) {

        Select(mockData)
          .font({ size: $r('app.float.body_font_size') })
          .selectedOptionFont({ size: $r('app.float.body_font_size') })
          .optionFont({ size: $r('app.float.body_font_size') })
          .value(this.countryCode)
          .layoutWeight(Constants.LENGTH_1_PX)
          .backgroundColor($r('app.color.placeholder_background'))
          .borderRadius(Constants.BORDER_RADIUS_4_PX)
          .height(Constants.HEIGHT_40)
          .onSelect((_, val) => {
            this.countryCode = val.substring(0, val.length - 4);
          })


        TextInput({ placeholder: $r('app.string.auth_dialog_number_placeholder') })
          .layoutWeight(Constants.LENGTH_3_PX)
          .margin({ left: Constants.LENGTH_5_PX })
          .borderRadius(Constants.BORDER_RADIUS_4_PX)
          .maxLength(Constants.LENGTH_20_PX)
          .height(Constants.HEIGHT_40)
          .enabled(this.timer === 60)
          .onChange((val) => {
            this.phoneNumber = val;
          })

      }
      .margin({ bottom: Constants.LENGTH_20_PX })

      Flex({ direction: FlexDirection.Row }) {
        TextInput({ placeholder: $r('app.string.auth_dialog_code_placeholder'), text: this.verificationCode })
          .layoutWeight(Constants.LENGTH_3_PX)
          .margin({ right: Constants.LENGTH_5_PX })
          .borderRadius(Constants.BORDER_RADIUS_4_PX)
          .maxLength(Constants.LENGTH_6_PX)
          .height(Constants.HEIGHT_40)
          .onChange((val) => {
            this.verificationCode = val;
          })

        Button(this.timer === 60 ? $r('app.string.auth_dialog_get_code_button_text') : this.timer.toString(), {
          type: ButtonType.Normal
        })
          .backgroundColor($r('app.color.start_window_background'))
          .layoutWeight(Constants.LENGTH_2_PX)
          .borderColor($r('app.color.action_button_background'))
          .borderWidth(Constants.LENGTH_1_PX)
          .fontColor($r('app.color.action_button_background'))
          .borderRadius(Constants.BORDER_RADIUS_4_PX)
          .margin({ left: Constants.LENGTH_5_PX })
          .height(Constants.HEIGHT_40)
          .enabled(this.canGetCode() && this.timer === 60)
          .onClick(() => this.onGetCodeButtonClicked())
      }
      .margin({ bottom: Constants.LENGTH_20_PX })


      Button($r('app.string.auth_dialog_auth_button_text'), { type: ButtonType.Normal })
        .width(Constants.PERCENT_100)
        .borderRadius(Constants.BORDER_RADIUS_4_PX)
        .backgroundColor($r('app.color.action_button_background'))
        .enabled(this.canAuthorize() && this.verificationCode.length > 5 && this.canLogin)
        .opacity(this.canLogin ? 1 : 0.5)
        .height(Constants.HEIGHT_40)
        .onClick(() => this.onAuthButtonClicked())

    }
    .height(Constants.PERCENT_50)
    .padding({ right: Constants.LENGTH_20_PX, left: Constants.LENGTH_20_PX })
  }

6.2用户登录成功信息写入缓存

调用自定义的登录接口实现登录,并使用首选项自定义工具接口将用户信息写入缓存。,这样下次进入之后,就可以进行对应的逻辑判断。执行对应的流程。

  onAuthButtonClicked() {
    this.canLogin = false;
    this.agcAuth.login(this.countryCode, this.phoneNumber, this.verificationCode).then(user => {
      AppStorage.Set<AgUser>('user', user);
      PreferencesUtil.putPreference(getContext(this), Constants.USER_AUTH_INFO, JSON.stringify(user));
      Log.info(TAG, "Logged in succesfully.");
      this.canLogin = true;
      router.replaceUrl({
        url: "pages/MainPage"
      })
    }).catch((err) => {
      this.canLogin = true;
      Log.error(TAG, "Logged in failed " + JSON.stringify(err));
      AlertDialog.show({
        title: $r('app.string.common_prompt'),
        message: $r('app.string.common_login_fail'),
      });
    });
  }

image-20230724091341770

七,元服务开发

元服务 (原为"原子化服务") 是一种基于HarmonyOS API的全新服务提供方式,以鸿蒙万能卡片等多种呈现形态, 向用户提供更轻量化的服务。具有即用即走、信息外显、服务直达的特性。

万能卡片(以下简称“卡片”)是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。卡片常用于嵌入到其他应用(当前卡片使用方只支持系统应用,如桌面)中作为其界面显示的一部分,并支持拉起页面、发送消息等基础的交互功能。

下面是我们开发卡片时候的实现原理

WidgetPrinciple

这里涉及的几个知识点给大家介绍一下

  • 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
  • 卡片提供方:提供卡片显示内容的应用,控制卡片的显示内容、控件布局以及控件点击事件。
  • 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务。
  • 卡片渲染服务:用于管理卡片渲染实例,渲染实例与卡片使用方上的卡片组件一一绑定。

ArkTS卡片提供了postCardAction()接口用于卡片内部和提供方应用间交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。这个我们在查询油价,以及处理message跳转的时候会用到。大家继续往后看就可以。

7.1油价查询(2*2)

这里我们需要学习的知识点在于卡片的创建,以及卡片数据如何交互。

创建ArkTS卡片有两种方式:

  • 通过在”entry“目录右键单击“New > Service Widget”创建卡片。
  • 通过在”entry > src/main/ets > widget > pages“目录右键单击“New > ArkTS File”创建文件,并在卡片配置文件form_config.json中配置卡片信息。

使用第二种方式创建卡片,在”entry > src/main/ets > widget > pages“目录右键单击“New > ArkTS File”创建RubbishWidgetCard.ets文件,接着打开"entry > src/main/resources > base > profile"目录下的form_config.json文件,配置名称为rubbish的2*2卡片

这样的话,我们的卡片就算创建完成了,

 Column() {
      Row() {
        Image($r("app.media.rubbish")).width(this.Image_WIDTH_PERCENT).margin({
          right: 5
        })
        Text(this.TITLE)
          .fontSize($r('app.float.font_size'))
          .fontWeight(FontWeight.Bold)
          .fontColor($r('app.color.item_title_font'))
      }
    }
    .width(this.FULL_WIDTH_PERCENT)
    .backgroundColor($r("app.color.emuiLow4_alpha"))

    .height(this.FULL_HEIGHT_PERCENT)
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      postCardAction(this, {
        "action": this.ACTION_TYPE,
        "abilityName": this.ABILITY_NAME,
        "params": {
          "targetPage": this.MESSAGE
        }
      });
    })
  }

但是要实现卡片数据的更新

ArkTS卡片框架提供了updateForm()接口和requestForm()接口主动触发卡片的页面刷新。

WidgetLocalStorageProp

接口 是否系统能力 约束
updateForm 1. 提供方调用。 2. 提供方仅允许刷新自己的卡片,其他提供方的卡片无法刷新。
requestForm 1. 使用方调用。 2. 仅允许刷新添加到当前使用方的卡片,添加到其他使用方的卡片无法刷新。

卡片中需要使用@LocalStorageProp装饰器接收。

@LocalStorageProp("puzzles") puzzles: Array<Array<number>> = [];
@LocalStorageProp("answers") answers: Array<Array<Array<number>>> = [];

然后,我们以更新深圳的天气为例,在/src/main/ets/entryformability/EntryFormAbility.ts写写对应的请求事件。

  upData(formId :string){
    let formInfo = formBindingData.createFormBindingData({
      'text': '刷新中...'
    })
    formProvider.updateForm(formId, formInfo)
    console.info('Result:的结果' + JSON.stringify(formId));
    let httpRequest = http.createHttp();
    httpRequest.request(Constants.OILSERVER, {
      method: http.RequestMethod.GET,
      readTimeout: Constants.HTTP_READ_TIMEOUT,
      connectTimeout: Constants.HTTP_READ_TIMEOUT
    , extraData: {
        'province': "深圳",
        'app_id': Constants.APPID,
        'app_secret': Constants.APPSECRET

      },
    }, (err, data) => {
      if (!err) {
        // data.result为HTTP响应内容,可根据业务需要进行解析
        console.info('Result:的结果' + JSON.stringify(data.result));
        let OilModel: OilModel = JSON.parse(data.result.toString())
        // 注意:FormExtensionAbility在触发生命周期回调时被拉起,仅能在后台存在5秒
        // 建议下载能快速下载完成的小文件,如在5秒内未下载完成,则此次网络图片无法刷新至卡片页面上
        let formData = {
          'oil': OilModel.data,
          'loaded': true,
          "time": getTime()

        };
        let formInfo = formBindingData.createFormBindingData(formData)
        formProvider.updateForm(formId, formInfo).then((data) => {
          console.info('FormAbility updateForm success.' + JSON.stringify(data));
        }).catch((error) => {
          console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
        })
        // 当该请求使用完毕时,调用destroy方法主动销毁
        httpRequest.destroy();
      } else {
        console.info('error:' + JSON.stringify(err));
        // 取消订阅HTTP响应头事件
        httpRequest.off('headersReceive');
        // 当该请求使用完毕时,调用destroy方法主动销毁
        httpRequest.destroy();
      }
    }

    );
  }

然后我们在onUpdateForm中去调用就可以啦。

  onUpdateForm(formId) {
    this.upData(formId)

    // Called to notify the form provider to update a specified form.
  }

这样我们就完成了对油价的查询。

这里我们主要了解的就是对卡片事件能力的处理。

WidgetPostCardAction

接口定义:postCardAction(component: Object, action: Object): void

接口参数说明:

参数名 参数类型 必填 参数描述
component Object 当前自定义组件的实例,通常传入this。
action Object action的具体描述,详情见下表。

action参数说明:

Key Value 样例描述
"action" string action的类型,支持三种预定义的类型: - "router":跳转到提供方应用的指定UIAbility。 - "message":自定义消息,触发后会调用提供方FormExtensionAbility的onFormEvent()生命周期回调。 - "call":后台启动提供方应用。触发后会拉起提供方应用的指定UIAbility(仅支持launchType为singleton的UIAbility,即启动模式为单实例的UIAbility),但不会调度到前台。提供方应用需要具备后台运行权限(ohos.permission.KEEP_BACKGROUND_RUNNING)。
"bundleName" string "router" / "call" 类型时跳转的包名,可选。
"moduleName" string "router" / "call" 类型时跳转的模块名,可选。
"abilityName" string "router" / "call" 类型时跳转的UIAbility名,必填。
"params" Object 当前action携带的额外参数,内容使用JSON格式的键值对形式。"call" 类型时需填入参数'method',且类型需要为string类型,用于触发UIAbility中对应的方法,必填。
7.1.1通过message事件刷新卡片内容

实现功能:在卡片上实现当前城市的油价查询,在页面里实现对各个城市油价的查询

主要技术,通过message事件刷新卡片内容,在卡片页面中可以通过postCardAction接口触发message事件拉起FormExtensionAbility,然后由FormExtensionAbility刷新卡片内容,展示油价信息

 .onClick(() => {
            postCardAction(this, {
              'action': 'message',
              'params': {
                'info': 'refreshImage'
              }
            });
          })

image-20230726004657917

坚果gif1

7.2垃圾分类(1*2)

这里实现的功能是点击卡片之后,进入垃圾分类查询页的详情。输入物品名称。就可以查看具体的垃圾分类信息。

7.2.1使用router事件跳转到指定UIAbility
.onClick(() => {
  postCardAction(this, {
   'action': 'router',
    'abilityName': 'EntryAbility',
    "params": {
      "targetPage": this.MESSAGE
    }
  });
})

然后我们在src/main/ets/entryability/EntryAbility.ts中处理对应的逻辑。

onCreate(want) {


  console.info("onCreate want:" + JSON.stringify(want));
  if (want.parameters.params !== undefined) {
    let params = JSON.parse(want.parameters.params);
    hilog.info(0x0000, 'tonCreate router targetPage', '%{public}s', params.targetPage)
    console.info("onCreate router targetPage:" + params.targetPage);
    selectPage = params.targetPage;
  }
}

如果UIAbility已在后台运行,在收到Router事件后会触发onNewWant生命周期回调

onNewWant(want, launchParam) {
  console.info("onNewWant want:" + JSON.stringify(want));
  if (want.parameters.params !== undefined) {
    let params = JSON.parse(want.parameters.params);
    hilog.info(0x0000, 'tonCreate router targetPage', '%{public}s', params.targetPage)

    console.info("onNewWant router targetPage:" + params.targetPage);
    console.info("获取的 params.targetPage是" + params.targetPage);
    if (params.dimension !== undefined && params.dimension != null) {
      AppStorage.Set<string>('message', params.dimension);
      console.info("保存的 params.targetPage是" + params.targetPage);
    }

    selectPage = params.targetPage;


  }
  if (currentWindowStage != null) {
    this.onWindowStageCreate(currentWindowStage);
  }
}

根据传递的targetPage不同,选择拉起不同的页面

onWindowStageCreate(windowStage: Window.WindowStage) {
  let targetPage;

  switch (selectPage) {
    case "MainPage":
      targetPage = "pages/MainPage";
      break;
    default:
      targetPage = 'pages/LoginPage';
  }
  if (currentWindowStage === null) {
    currentWindowStage = windowStage;
  }
  windowStage.loadContent(targetPage, (err, data) => {
    if (err && err.code) {
      console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err));
      return;
    }
  });
}

这样我们就完成了跳转。

image-20230803233003756

坚果gif2

7.3当前定位(2*2)

点击卡片之后,进入定位展示的详情页,展示当前的详细定位。

主要技术,动态权限的获取。

7.3.1动态权限获取

首先需要在module.json中声明对应权限

 "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.MEDIA_LOCATION",
      },
      {
        "name": 'ohos.permission.APPROXIMATELY_LOCATION'
      },
      {
        "name": 'ohos.permission.LOCATION'
      }
    ],

然后在ets/entryability/EntryAbility.ts中添加动态权限的申请

    let AtManager = abilityAccessCtrl.createAtManager();
    AtManager.requestPermissionsFromUser(this.context, [  'ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION']).then((data) => {
      hilog.info(0x0000, 'testTag', '%{public}s', 'request permissions from user success' + data);
    }).catch((err) => {
      hilog.error(0x0000, 'testTag', 'Failed to request permissions from user. Cause: %{public}s', JSON.stringify(err) ?? '');
    });
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');

然后我们编写卡片代码

    Column() {
      Text("你想知道")
        .fontSize($r('app.float.title_immersive_font_size'))
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .fontColor($r('app.color.text_font_color'))
        .maxLines(this.MAX_LINES)
      Text("你当前的位置吗")
        .fontSize("14fp")
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .fontColor($r('app.color.text_font_color'))
        .maxLines(this.MAX_LINES)
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .padding($r('app.float.column_padding'))

    .width(this.FULL_WIDTH_PERCENT)
    .height(this.FULL_HEIGHT_PERCENT)
    .backgroundColor($r('app.color.emuiLow2_alpha'))
    .onClick(() => {
      postCardAction(this, {
        "action": this.ACTION_TYPE,
        "abilityName": this.ABILITY_NAME,
        "params": {
          "targetPage": this.MESSAGE
        }
      });
    })
  }

运行之后的效果

image-20230724091309390

image-20230724091657920

坚果gif3

八,总结

本文我们从元服务的基本概念,到我们实用小工具的项目开发,技术剥析,完整的了解了端元一体化开发元服务,以及如何在元服务中集成华为认证服务,到最后完成油价查询功能在卡片上及时显示,定位在卡片上实时刷新。以及点击卡片分类,还可以进入详情页查询更多物品的垃圾属性,以及动态权限的获取。

你可以学到的有

1.了解元服务的基本概念

2.使用端云一体化开发、开发云函数、开发云数据库,集成华为认证服务。

3.使用FormExtensionAbility创建、更新、删除元服务卡片。

4.使用router、message和call三种类型的事件,处理对应逻辑。

也可点击元服务官网,了解更多信息。

本文作者:鸿蒙坚果派

想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com/#bkwz​

标签:targetPage,鸿蒙,卡片,app,开发,params,工具箱,Constants,函数
From: https://blog.51cto.com/harmonyos/7120440

相关文章

  • 社区版idea 开发springmvc踩坑指南
    说明:社区版的idea不支持spring和web,所以在开发时跟旗舰版有些许不同,不同于旗舰版在加载spring,web子模块时社区版需要手动加载tomcat下的所有lib项目以及pro下加载的所有的依赖项lib到libraries中。此外社区版idea在springmvc开发时由于不直接支持web项目所以需要在pro项目的src目录......
  • Vuejs装饰器风格开发教程(计算属性、事件派发、侦听器)
    计算属性计算属性的设计背景:在Vuejs开发时我们可以在模板中通过编写表达式的方式做一系列的逻辑处理,但这就偏离的模板的概念,还会使得模板的内容变得臃肿且难以维护,所以引入了计算属性的来对不该出现在模板中的复杂逻辑处理进行重构,使用计算属性重构后的依然保持了状态的响应式......
  • 跨平台开发:让你的直播带货App覆盖更多用户
    随着移动互联网的迅猛发展,直播带货成为了商业模式的一大亮点。无论是品牌还是个人商家,都希望通过直播带货来吸引更多用户,增加销售额。然而,要实现这一目标,一个重要的考虑因素就是如何让你的直播带货App覆盖更多的用户。跨平台开发技术就成为了一个解决方案,它可以帮助你的App在不同的......
  • 低代码软件开发框架:用专业技术开启流程化管理办公!
    在新的发展时代,拥有优质的资源和平台,可以助力企业创造佳绩,实现梦想。低代码软件开发框架是提升办公协作效率,提质增效的软件平台,在很多大中型企业中深受欢迎,可以帮助企业实现流程化管理,进入数字化发展新时代。诚然,选择好的服务商是走向成功的关键一步。因为这是能给企业降低风险、......
  • [BitSail] Connector开发详解系列三:SourceReader
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群SourceConnector本文将主要介绍负责数据读取的组件SourceReader:SourceReader每个SourceReader都在独立的线程中执行,只要我们保证SourceSplitCoordinator分配给不同SourceReader的切片没有交集,在S......
  • [BitSail] Connector开发详解系列三:SourceReader
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群SourceConnector本文将主要介绍负责数据读取的组件SourceReader:SourceReader每个SourceReader都在独立的线程中执行,只要我们保证SourceSplitCoordinator分配给不同SourceReader的切......
  • flask开发笔记
    1、调试参考:https://deepinout.com/flask/flask-questions/29_flask_how_to_debug_a_flask_app.html用pdb感觉还不错fromflaskimportFlaskimportpdbapp=Flask(__name__)[email protected]('/')defhello_world():name="Alice"......
  • PyTorch神经网络工具箱-新手笔记
    训练模型构建模型后,接下来就是训练模型。PyTorch训练模型的主要步骤包括加载和预处理数据集、损失计算、定义优化算法、反向传播、参数更新等主要步骤。1)加载和预处理数据集:可以使用PyTorch的数据处理工具,如torch.utils和torchvision等。2)定义损失函数:通过自定义的方法或使用PyTorc......
  • [18章]Vue3+NestJS 全栈开发企业级管理后台
    点击下载:[18章]Vue3+NestJS全栈开发企业级管理后台提取码:zzbv Next.js是一个用于构建现代化React应用程序的框架。它强调性能、开发体验和SEO优化,是许多React开发者的首选。Next.js提供了许多功能,包括:服务器渲染:Next.js允许在服务器端渲染React应用程序,从而提高了应......
  • 百度王海峰披露飞桨生态最新成果 开发者数量已达800万
    8月16日,由深度学习技术及应用国家工程研究中心主办的WAVESUMMIT深度学习开发者大会2023在北京举行。百度首席技术官、深度学习技术及应用国家工程研究中心主任王海峰做了主题演讲。王海峰首次对外表示,大语言模型具备了理解、生成、逻辑、记忆等人工智能的核心基础能力,为通用人工智......