赞
踩
数独闯关游戏元服务,支持2*4历史闯关记录卡片、4*4数独闯关游戏卡片,无需进入元服务,可在桌面上开始游戏,同步刷新记录。
选择以下一种方式,打开工程创建向导界面
在Choose Your Ability Template向导页,单击“Atomic Service”切换工程类型,选择云开发模板,单击“Next”进入下一步。
在工程配置界面,配置工程的基本信息。单击“Next”按钮进入关联云开发资源界面。
参数 | 说明 |
---|---|
Project name | 工程的名称,由大小写字母、数字和下划线组成。 |
Bundle name | 软件包名称,需保证唯一,由字母、数字、下划线和符合“.”组成,以字母开头,长度为7-127个字符。 |
Save location | 工程文件本地存储路径,由大小写字母、数字和下划线等组成,不能包含中文字符。 |
Compile SDK | 应用的目标API Version,在编译构建时,DevEco Studio会根据指定的Compile API版本进行编译打包。使用云开发模板,版本不低于9。 |
Model | 应用支持的模式,云开发模板仅支持Stage模式。 |
Language | 开发语言。Stage模式仅支持使用ArkTS语言开发。 |
Compatible SDK | 兼容的最低API Version。云开发模板,版本不低于9。 |
Device Type | 工程模板支持的设备类型,当前API 9仅支持手机设备。 |
Enable CloudDev | 是否启用云开发,默认启用且无法更改。 |
为工程关联云开发所需的资源,即在DevEco Studio中选择您的华为开发者账号加入的开发者团队,将该团队在AGC的同包名应用关联到当前工程,具体操作如下:
登录成功后,在授权界面单击“允许”按钮为DevEco Studio授权,界面将展示账号昵称。
单击“AppGallery Connect”打开AGC应用创建向导,填写应用信息,单击“确认”按钮创建应用。
完成以上操作后,DevEco Studio即可获取到同包名应用对应的项目信息。
成功创建工程并关联云开发资源后,DevEco Studio会为工程自动执行一些初始化配置,并开通云开发相关服务:认证服务、云函数、云数据库、云托管、API网关、云存储。
entry/src/main/resources/rawfile/agconnect-services.json
和在entry/oh-package.json5
配置文件中引入的AGC相关云服务最新版本SDK。端云一体化元服务开发工程目录分为三个子工程:元服务开发工程(Application)、云开发工程(CloudProgram)、端侧公共库(External Libraries)。
元服务开发工程主要用于开发应用端侧的业务代码,元服务开发工程目录结构如下:
- - 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 // 应用级编译构建任务脚本
复制
云开发工程中开发者可以为应用开发云函数和云数据库服务资源,云开发工程目录结构如下:
- - CloudProgram
- - clouddb // 云数据库工程目录
- dataentry // 用于存放数据条目文件
- objecttype // 用于存放对象类型文件
- db-config.json // 模块配置文件
- - cloudfunctions // 云函数工程目录
- cloudFunctionName // 云函数名称
- node_modules // 包含所有三方依赖
- cloud-config.json // 云开发工程配置文件
- package.json // 定义了TypeScript公共依赖
复制
在云端工程(CloudProgram)中可以创建函数、编写函数业务代码、为函数配置调用触发器。
为当前工程创建生成数独9*9宫格二维数组算法,云函数名称为
sudoku-algorithm
。
- - sudoku-algorithm
- node_modules // 自动为该函数引入依赖包
- function-config.json // 函数的配置文件,可配置触发器,通过触发器暴露的触发条件来实现函数调用。
- package.json // 包含了当前函数的名称、版本等函数元数据。
- sudoku-algorithm.ts // 函数入口文件
复制
function-config.json
文件中triggers
属性中配置,当前支持HTTP触发器、CLOUDDB触发器、AUTH触发器、CLOUDSTORAGE触发器、CRON触发器五种。function-config.json
文件生成HTTP触发器配置。函数部署到云端后会自动生成触发URL,开发者向URL发起HTTP请求时触发函数。- {
- "type": "http",
- "properties": {
- "enableUrlDecode": true,
- "authFlag": "true",
- "authAlgor": "HDA-SYSTEM",
- "authType": "apigw-client"
- }
- }
复制
参数 | 说明 |
---|---|
enableUrlDecode | 通过HTTP触发器触发函数,对于contentType为“application/x-www-form-urlencoded”的触发请求,是否使用URLDecoder对请求body进行解码再转发到函数中。 - true:启用。 - false:不启用。 |
authFlag | 是否鉴权,默认为true。 |
authAlgor | 鉴权算法,默认为HDA-SYSTEM。 |
authType | HTTP触发器的认证类型。 - apigw-client:端侧网关认证,适用于来自APP客户端侧的函数调用。 - cloudgw-client:云侧网关认证,适用于来自APP服务器侧的函数调用。 |
云函数的代码实现基于不同的语言运行环境可分为Node.js、Java、Python,还有一种比较特别运行环境为Custom Runtime(自定义运行环境)。本工程的语言运行环境为Node.js。
module.exports.myHandler = function(event, context, callback, logger)
复制
函数必须通过显示调用callback(object)将事件处理结果返回给AGC,结果可以是任意对象,但必须与JSON.stringify兼容,AGC会将结果转换成JSON字符串,返回给调用方。callback执行完成后,函数即执行结束。
- let myHandler = async function (event, context, callback, logger) {
- logger.info(event);
-
- // do something here
- callback({
- code: 0,
- desc: "Success.",
- data: "请求成功!"
- });
- };
- export { myHandler };
复制
函数开发过程中,开发者可在本地进行调试,或者将函数部署到AGC云端后,在本地触发调用云端函数。当前本地调试支持Run和Debug两种模式,Debug模式支持使用断点来追踪函数的运行情况。
当开发者创建的函数或函数别名中创建一个HTTP类型的触发器后,在应用客户端调用函数时需要传入HTTP触发器的标识,查询方法如下:在函数的触发器页面点击“HTTPTrigger”触发器,查看“触发URL”的后缀,获取触发器标识,格式为“函数名-版本号”。
应用集成云函数SDK后,可以在应用内直接通过SDK API调用AGC中的云函数,云函数SDK与AGC的函数调用基于HTTPS的安全访问。
SudokuAlgorithmFunction.ts
文件,编写调用云函数方法。- import agconnect from '@hw-agconnect/api-ohos';
- import "@hw-agconnect/function-ohos";
- import { Constants } from '../common/Constants';
- import { Log } from '../common/Log';
- import { getAGConnect } from './AgcConfig';
-
- export function getSudokuPuzzle(context: any) {
- return new Promise((resolve, reject) => {
- getAGConnect(context);
- // 调用wrap方法设置函数,在方法中传入触发器标识,返回得到可执行云函数对象
- let functionCallable = agconnect.function().wrap("sudoku-algorithm-$latest");
- // 调用call方法运行云函数,若函数有入参,可以将参数转化为JSON对象或JSON字符串传入,若无参则不传
- functionCallable.call().then((ret: any) => {
- // 可调用getValue方法获取函数的返回值
- let result = ret.getValue();
- Log.info(Constants.LOG_TAG_NAME, `sudoku-algorithm called, result: ${JSON.stringify(result)}`);
- resolve(result);
- }).catch((err: any) => {
- Log.error(Constants.LOG_TAG_NAME, `sudoku-algorithm failed, cause: ${JSON.stringify(err)}`);
- })
- });
- }
复制
- Button('请求自定义云函数')
- .fontSize(16)
- .onClick(() => {
- getSudokuPuzzle(getContext(this)).then((ret) => {
- Log.info(Constants.LOG_TAG_NAME, `单击按钮调用云函数返回结果: ${JSON.stringify(ret)}`)
- })
- })
复制
使用回溯算法填充数独谜题,并随机移除一些数字将其作为数独谜题,然后求解指定数独谜题的所有解。
- let myHandler = async function (event, context, callback, logger) {
- // 传递的关卡值作为需要填充的空格数
- let body = event.body;
- let params = JSON.parse(body);
- let levelNum = params.level;
- // 创建一个9*9的空白数独谜题
- let sudoku = Array.from({ length: 9 }, () => Array(9).fill(0));
- // 使用回溯算法填充数独谜题
- solve_sudoku(sudoku);
- // 随机移除一些数字,生成数独谜题
- remove_number(sudoku, levelNum);
- let solutions = answer_sudoku(sudoku);
- let sudokuPuzzle = {
- "original": sudoku,
- "answer": solutions
- }
- callback({
- code: 0,
- desc: "Success.",
- data: JSON.stringify(sudokuPuzzle)
- });
- };
-
- function solve_sudoku(sudoku){...}
- function remove_number(sudoku, level){...}
- function answer_sudoku(sudoku){...}
-
- export { myHandler };
复制
当前AGC认证服务为HarmonyOS应用/服务提供的登录认证方式有手机、邮箱和关联账号三种方式。本工程使用“邮箱+验证码”的方式作为应用的登录入口。
AGConnectAuth.requestEmailVerifyCode
申请验证码,在entry/src/main/ets/services/Auth.ts
认证工具类中添加邮箱验证码获取方法。- // 申请邮箱验证码
- public requestEmailVerifyCode(email: string) {
- let verifyCodeSettings = new VerifyCodeSettingBuilder()
- .setAction(VerifyCodeAction.REGISTER_LOGIN)
- .setLang('zh_CN')
- .setSendInterval(60)
- .build();
- this.agc.auth().requestEmailVerifyCode(email, verifyCodeSettings)
- .then((ret) => {
- Log.info(TAG, JSON.stringify({ "Verify Code Result: ": ret }));
- }).catch((error) => {
- Log.error(TAG, "Error: " + JSON.stringify(error));
- });
- }
复制
EmailUserBuilder
生成EmailUser
,然后调用AGConnectAuth.createEmailUser
注册用户。注册成功后,系统会自动登录,无需再次调用登录接口。也可以使用signIn登录接口,通过第三方认证来登录AGConnect平台,在entry/src/main/ets/services/Auth.ts
认证工具类中添加邮箱账号注册用户方法。- // 邮箱账号注册登录
- public async loginByEmail(email: string, verifyCode: string): Promise<AgUser> {
- return new Promise((resolve, reject) => {
- // 如果创建账户的时候没有设置过密码,则只能通过此接口进行登录
- let credential = EmailAuthProvider.credentialWithVerifyCode(email, verifyCode);
- // 登录接口,通过第三方认证来登录AGConnect平台
- this.agc.auth().signIn(credential).then(async (ret) => {
- Log.info(TAG, `User has signed in. User: ${JSON.stringify(ret)}`);
- 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);
- });
- });
- }
复制
通过容器组件Flex、Row、Column以及基础组件Text、Image、Button、Navigation、TextInput构建邮箱登录页面。
- Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- Navigation() {
- Column() {
- Row({ space: Constants.LENGTH_5_PX }) {
- TextInput({ placeholder: '请输入邮箱账号..'})
- .type(InputType.Email)
- .layoutWeight(Constants.LENGTH_3_PX)
- .borderRadius(Constants.BORDER_RADIUS_4_PX)
- .maxLength(Constants.LENGTH_20_PX)
- .height(Constants.HEIGHT_40)
- .enabled(this.timer === 60)
- .onChange((val) => {
- this.email = val;
- })
- }
- .width(Constants.PERCENT_100)
- .justifyContent(FlexAlign.Center)
- .margin({ bottom: Constants.LENGTH_20_PX })
-
- Row({ space: Constants.LENGTH_5_PX }) {
- TextInput({ placeholder: '请输入验证码..', text: this.verificationCode })
- .layoutWeight(Constants.LENGTH_3_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 ? '获取验证码' : this.timer.toString(), {
- type: ButtonType.Normal
- })
- .backgroundColor('#f9fcfb')
- .layoutWeight(Constants.LENGTH_2_PX)
- .borderColor('#169cd5')
- .borderWidth(Constants.LENGTH_1_PX)
- .fontColor('#169cd5')
- .borderRadius(Constants.BORDER_RADIUS_4_PX)
- .height(Constants.HEIGHT_40)
- .enabled(this.validateEmailAddress(this.email) && this.timer === 60)
- .onClick(() => this.onGetCodeButtonClicked())
- }
- .width(Constants.PERCENT_100)
- .justifyContent(FlexAlign.Center)
- .margin({ bottom: Constants.LENGTH_20_PX })
-
- Button('登录', { type: ButtonType.Normal })
- .width(Constants.PERCENT_100)
- .borderRadius(Constants.BORDER_RADIUS_4_PX)
- .backgroundColor('#169cd5')
- .enabled(this.canAuthorize() && this.verificationCode.length > 5 && this.canLogin)
- .opacity(this.canLogin ? 1 : 0.5)
- .height(Constants.HEIGHT_40)
- .onClick(() => this.onAuthButtonClicked())
- }
- .width(Constants.PERCENT_90).height(Constants.HEIGHT_50)
- .justifyContent(FlexAlign.Center)
- .margin({ top: Constants.LENGTH_40_PX })
- .padding({
- right: Constants.LENGTH_15_PX,
- left: Constants.LENGTH_15_PX
- })
- .borderRadius(Constants.LENGTH_8_PX)
- .backgroundColor(0xFFFFFF)
- }
- .title(this.NavigationTitle())
- .titleMode(NavigationTitleMode.Full)
- .hideTitleBar(false)
- .hideToolBar(false)
- }
- .width(Constants.PERCENT_100).height(Constants.PERCENT_100)
- .backgroundColor(Constants.VIEW_BG_COLOR)
复制
调用自定义的登录接口实现登录,并使用首选项自定义工具接口将用户信息写入缓存。
- onAuthButtonClicked = () => {
- this.canLogin = false;
- this.agcAuth.loginByEmail(this.email, this.verificationCode).then((user) => {
- PreferencesUtil.putPreference(getContext(this), Constants.USER_AUTH_INFO, JSON.stringify(user));
- Log.info(Constants.LOG_TAG_NAME, `Logged in successfully. Data: ${JSON.stringify(user)}`);
- this.canLogin = true;
- }).catch((err) => {
- this.canLogin = true;
- Log.error(Constants.LOG_TAG_NAME, `Logged in failed. Cause: ${JSON.stringify(err)}`);
- })
- }
复制
右键单击“entry > src/main/ets > pages”目录选择“New > Pages”新建Setting
设置页面。
在页面中使用容器组件Grid实现头像选择(提供可选头像6个)和使用基础组件TextInput实现昵称设置。
头像昵称设置成功后,跳转到游戏主界面,点击“开始”按钮从云函数中获取数独谜题及对应的解,然后通过容器组件Grid和其子组件GridItem构建9*9宫格并使用ForEach渲染宫格的对应组件。
- // 获取数独谜题和解
- getSudoPuzzle = () => {
- let _this = this;
- getSudokuPuzzle(getContext(this), this.levelNum).then((ret: string) => {
- let result: SudokuPuzzle = JSON.parse(ret);
- _this.puzzles = result.original;
- _this.answers = result.answer;
- }).catch((err) => {
- Log.error(Constants.LOG_TAG_NAME, `cause: ${JSON.stringify(err)}`);
- })
- }
-
- Grid() {
- ForEach(this.puzzles, (item: Array<number>, i: number) => {
- ForEach(item, (temp: number, j: number) => {
- GridItem() {
- if (temp === 0) {
- TextInput()
- .type(InputType.Number)
- .maxLength(1)
- .backgroundColor(0xf47721)
- .caretColor(Color.White)
- .onChange((val) => {
- let answer = this.puzzles;
- answer[i][j] = parseInt(val);
- this.userAnswer = answer;
- Log.info(Constants.LOG_TAG_NAME, JSON.stringify(this.userAnswer));
- if (val == "") {
- this.userAnswer = [];
- }
- })
- } else {
- Text(temp.toString())
- .fontSize(16)
- }
- }
- .borderWidth(1)
- }, (temp: number) => temp.toString())
- }, (item: Array<number>) => item.toString())
- }
- .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr')
- .rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr')
- .width(Constants.PERCENT_96)
- .height(400)
- .backgroundColor(0xFFFFFF)
复制
万能卡片(以下简称“卡片”)是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。卡片常用于嵌入到其他应用(当前卡片使用方只支持系统应用,如桌面)中作为其界面显示的一部分,并支持拉起页面、发送消息等基础的交互功能。
工程在创建初会自动创建2*2服务卡片,位于“entry > src/main/ets > widget > pages”目录。在WidgetCard.ets
文件中编写服务卡片呈现内容及样式。
- Column() {
- Image($r("app.media.card_start"))
- .width(this.FULL_WIDTH_PERCENT)
- .height(this.FULL_HEIGHT_PERCENT)
- .objectFit(ImageFit.Cover)
- }
- .width(this.FULL_WIDTH_PERCENT)
- .height(this.FULL_HEIGHT_PERCENT)
- .onClick(() => {
- postCardAction(this, {
- "action": this.ACTION_TYPE,
- "abilityName": this.ABILITY_NAME,
- "params": {
- "message": this.MESSAGE
- }
- });
- })
复制
ArkTS卡片提供了postCardAction()
接口用于卡片内部和提供方应用间交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。
接口定义:postCardAction(component: Object, action: Object): void
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
component | Object | 是 | 当前自定义组件的实例,通常传入this。 |
action | Object | 是 | 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类型,用于触发UIAbiltiy中对应的方法,必填。 |
2*2万能卡片提供点击卡片进入元服务主界面。
4*4服务卡片用于在桌面玩游戏,没关通关后需要通过message事件刷新卡片内容生成新的关卡。
创建ArkTS卡片有两种方式:
form_config.json
中配置卡片信息。 使用第二种方式创建卡片,在”entry > src/main/ets > widget > pages“目录右键单击“New > ArkTS File”创建GameCard.ets
文件,接着打开"entry > src/main/resources > base > profile"目录下的form_config.json
文件,配置名称为game
的4*4卡片。- {
- "name": "game",
- "description": "数独闯关游戏",
- "src": "./ets/widget/pages/GameCard.ets",
- "uiSyntax": "arkts",
- "window": {
- "designWidth": 720,
- "autoDesignWidth": true
- },
- "colorMode": "auto",
- "isDefault": false,
- "updateEnabled": false,
- "scheduledUpdateTime": "10:30",
- "updateDuration": 1,
- "defaultDimension": "4*4",
- "supportDimensions": [
- "4*4"
- ]
- }
复制
卡片相关的配置主要包括FormExtensionAbility的配置和卡片的配置两部分:
- {
- "module": {
- ...
- "extensionAbilities": [
- {
- "name": "EntryFormAbility",
- "srcEntrance": "./ets/entryformability/EntryFormAbility.ts",
- "label": "$string:EntryFormAbility_label",
- "description": "$string:EntryFormAbility_desc",
- "type": "form",
- "metadata": [
- {
- "name": "ohos.extension.form",
- "resource": "$profile:form_config"
- }
- ]
- }
- ]
- }
- }
复制
$profile:form_config
时,会使用"entry > src/main/resources > base > profile"目录下的form_config.json
作为卡片profile配置文件。属性名称 | 含义 | 数据类型 | 是否可缺省 |
---|---|---|---|
name | 表示卡片的类名,字符串最大的长度为127字节。 | 字符串 | 否 |
description | 表示卡片的描述。取值可以时描述性内容,也可以是对描述内容的资源索引,以支持多语言。字符串最大长度为255字节。 该属性将显示在卡片预览界面上,以便用户识别不同的卡片。 | 字符串 | 可缺省,缺省为空。 |
src | 表示卡片对应的UI代码的完整路径。当为ArkTS卡片时,完整路径需要包含卡片文件的后缀,如:“./ets/widget/pages/WidgetCard.ets”。当为JS卡片式,完整路径无需包含卡片文件的后缀。 | 字符串 | 否 |
uiSyntax | 表示该卡片的类型,当前支持如下两种类型: - arkts:当前卡片为ArkTS卡片。 - hml:当前卡片为JS卡片。 | 字符串 | 可缺省,缺省为hml |
window | 用于定义与显示窗口相关的配置。 | 对象 | 可缺省 |
isDefault | 表示该卡片是否为默认卡片,每个UIAbility有且只有一个默认卡片。 - true:默认卡片。 - false:非默认卡片。 | 布尔值 | 否 |
colorMode | 表示卡片的主题样式,取值范围如下: - auto:自适应。 - dark:深色主题。 - light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 |
supportDimensions | 表示卡片支持的外观规格,取值范围: - 1*2:表示1行2列的二宫格。 - 2*2:表示卡片为2行2列的四宫格。 - 2*4:表示2行4列的八宫格。 - 4*4:表示4行4列的十六宫格。 | 字符串数组 | 否 |
defaultDimension | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 |
updateEnabled | 表示卡片是否支持周期性刷新(包含定时刷新和定点刷新),取值范围: - true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,当两者同时配置时,定时刷新优先生效。 - false:表示不支持周期性刷新。 | 布尔类型 | 否 |
scheduledUpdateTime | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。 说明:updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 字符串 | 可缺省,缺省时不进行定点刷新。 |
updateDuration | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。当取值为0时,表示该参数不生效。当取值为正整数N时,表示刷新周期为30*N分钟。 说明:updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 数值 | 可缺省,缺省值为“0”。 |
formConfigAbility | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 |
formVisibleNotify | 标识是否允许卡片使用卡片可见性通知。 | 字符串 | 可缺省,缺省值为空。 |
metadata | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 |
在卡片页面可以通过postCardAction
接口触发message事件拉起FormExtensionAbility,然后由FormExtensionAbility刷新卡片内容。
postCardAction
接口触发事件至FormExtensionAbility。- Image($r('app.media.game_start'))
- .width(120).height(54)
- .onClick(() => {
- postCardAction(this, {
- 'action': 'message',
- 'params': {
- 'functionName': 'getSudoPuzzle'
- }
- })
- })
复制
getSudokuPuzzle()
方法获取云函数生成的数独谜题和解,在刷新卡片时传递给卡片。- onFormEvent(formId, message) {
- let params = JSON.parse(message);
- Log.info(Constants.LOG_TAG_NAME, `message ===> ${params.functionName}`);
- // Called when a specified message event defined by the form provider is triggered.
- if (params.functionName === "getSudoPuzzle") {
- let promise = PreferenceUtil.getPreference(this.context, Constants.GAME_LEVEL);
- promise.then((ret) => {
- let level = parseInt(ret);
- let puzzles = [], answers = [];
- getSudokuPuzzle(this.context, level).then((ret: string) => {
- let result: SudokuPuzzle = JSON.parse(ret);
- puzzles = result.original;
- answers = result.answer;
- let formData = {
- flag: true,
- puzzles: puzzles,
- answers: answers,
- level: level
- }
- let formBD = formBindingData.createFormBindingData(formData);
- Log.info(Constants.LOG_TAG_NAME, `level. ${JSON.stringify(formBD)}`);
- formProvider.updateForm(formId, formBD).then((data) => {
- Log.info(Constants.LOG_TAG_NAME, `FormAbility updateForm success. ${JSON.stringify(data)}`);
- }).catch((err) => {
- Log.error(Constants.LOG_TAG_NAME, `FormAbility updateForm failed. ${JSON.stringify(err)}`);
- })
- }).catch((err) => {
- Log.error(Constants.LOG_TAG_NAME, `cause: ${JSON.stringify(err)}`);
- })
- })
- }
- }
复制
卡片中需要使用@LocalStorageProp
装饰器接收。
- @LocalStorageProp("puzzles") puzzles: Array<Array<number>> = [];
- @LocalStorageProp("answers") answers: Array<Array<Array<number>>> = [];
复制
ArkTS卡片具备JS卡片的全量能力,并且新增了动效能力和自定义绘制的能力,支持声明式的部分组件、事件、动效、数据管理、状态管理能力。在数独游戏中需要使用输入框录入谜题解,而ArkTS卡片暂时不具备TextInput组件能力,因此使用点击空白区域与数字按钮互换的方式替代TextInput组件能力。
- Row() {}
- .backgroundColor(0xD1D3D5)
- .width(this.FULL_HEIGHT_PERCENT)
- .height(this.FULL_HEIGHT_PERCENT)
- .onClick(() => {
- this.selectArr = [i, j];
- })
复制
postCardAction
将空白区域填入解的数组传递给FormExtensionAbility的onFormEvent方法。- ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9], (item: number) => {
- Button({ type: ButtonType.Normal }) {
- Text(item.toString()).fontSize(14).fontWeight(700)
- }
- .backgroundColor(Color.Orange)
- .borderRadius(4)
- .width(30).height(30)
- .fontSize(12)
- .onClick(() => {
- if (this.selectArr.length !== 0) {
- let row = this.selectArr[0];
- let col = this.selectArr[1];
- this.puzzles[row][col] = item;
- postCardAction(this, {
- 'action': 'message',
- 'params': {
- 'functionName': 'refresh',
- 'puzzles': this.puzzles
- }
- })
- this.userAnswer = this.puzzles;
- this.selectArr = [];
- }
- })
- })
复制
params.functionName.refresh
通过调用updateForm刷新卡片。- if (params.functionName === 'refresh') {
- let formData = {
- puzzles: params.puzzles
- }
- let formBD = formBindingData.createFormBindingData(formData);
- formProvider.updateForm(formId, formBD);
- }
复制
在应用程序中可以使用setInterval进行计时操作,但当前ArkTS卡片不支持setInterval,因此使用new Date().getTime()
开始时间和结束时间差值作为游戏时长替代setInterval方法。
使用第二种方式创建卡片,在"entry > src/main/ets > widget > pages"目录右键单击"New > ArkTS File"创建HistoryCard.ets
文件,接着打开"entry > src/main/resources > base > profile"目录下的form_config.json
文件,配置名称为history
的2*4卡片。
- {
- "name": "history",
- "description": "历史闯关记录",
- "src": "./ets/widget/pages/HistoryCard.ets",
- "uiSyntax": "arkts",
- "window": {
- "designWidth": 720,
- "autoDesignWidth": true
- },
- "colorMode": "auto",
- "isDefault": false,
- "updateEnabled": false,
- "scheduledUpdateTime": "02:35",
- "updateDuration": 1,
- "defaultDimension": "2*4",
- "supportDimensions": [
- "2*4"
- ]
- }
复制
- let formStorage = PreferenceUtil.getPreference(this.context, Constants.FORM_CARD_Dimension_2_4);
- formStorage.then((ret) =>{
- let formArr: Array<any> = new Array<any>();
- if (ret !== "") {
- formArr = JSON.parse(ret);
- formArr.push(formInfoStorage);
- } else {
- formArr.push(formInfoStorage);
- }
- PreferenceUtil.putPreference(this.context, Constants.FORM_CARD_Dimension_2_4, JSON.stringify(formArr));
- })
- let promise = PreferenceUtil.getPreference(this.context, Constants.HISTORY_RECORDS);
- promise.then((ret) => {
- if (ret !== "") {
- let historyArr: Array<History> = JSON.parse(ret);
- formData = {
- histories: historyArr
- }
- let formBD = formBindingData.createFormBindingData(formData);
- formProvider.updateForm(formId, formBD);
- return formBD;
- }
- })
复制
- PreferenceUtil.getPreference(this.context, Constants.HISTORY_RECORDS).then((ret) => {
- let historyArr: Array<History> = [];
- if (ret !== "") {
- historyArr = JSON.parse(ret);
- historyArr.push(history);
- } else {
- historyArr.push(history);
- }
- PreferenceUtil.putPreference(this.context, Constants.HISTORY_RECORDS, JSON.stringify(historyArr));
- let formStorage = PreferenceUtil.getPreference(this.context, Constants.FORM_CARD_Dimension_2_4);
- formStorage.then((ret) =>{
- if (ret !== "") {
- let formArr: Array<any> = JSON.parse(ret);
- formArr.forEach((item) => {
- let promise = PreferenceUtil.getPreference(this.context, Constants.HISTORY_RECORDS);
- promise.then((ret) => {
- if (ret !== "") {
- let historyArr: Array<History> = JSON.parse(ret);
- let formData = {
- histories: historyArr
- }
- let formBD = formBindingData.createFormBindingData(formData);
- formProvider.updateForm(item.formId, formBD);
- }
- })
- })
- }
- })
- })
复制
大家可以在华为应用市场元服务专区、服务中心入口,体验已经上架的元服务。
最后,为了能够让大家跟上互联网时代的技术迭代,赶上互联网开发人员寒冬期间一波红利,在这里跟大家分享一下我自己近期学习心得以及参考网上资料整理出的一份最新版的鸿蒙学习提升资料,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。