鸿蒙端云一体化,极简开发数独闯关游戏元服务_鸿蒙端云一体化有什么用

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)


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



![](https://img-blog.csdnimg.cn/img_convert/8f1995732ddcd46686199dd1bfb68809.png)



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

## 5 云函数开发指南


### 5.1 创建函数


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


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



![](https://img-blog.csdnimg.cn/img_convert/6a7f4893fc6c28651fd2f238dd1ad8d6.png)


1. 输入函数名称,单击“OK”按钮DevEco Studio自动生成函数目录。函数名称仅支持小写英文字母、数字、中划线(-),首字母必须为小写字母,结尾不能为中划线(-)。 
 
> 
> 为当前工程创建生成数独9\*9宫格二维数组算法,云函数名称为`sudoku-algorithm` 。
> 
> 
>



![](https://img-blog.csdnimg.cn/img_convert/509ce5e19701983adb5c27c4236cb625.png)


1. 云函数目录结构。



![](https://img-blog.csdnimg.cn/img_convert/70756c89610e552efcab5db4032417ef.png)



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

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


* HTTP触发器:工程创建完成后默认自动在`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服务器侧的函数调用。 |


### 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执行完成后,函数即执行结束。


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



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

// do something here
callback({
code: 0,
desc: “Success.”,
data: “请求成功!”
});
};
export { myHandler };
复制


1. 调试云函数


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


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



![](https://img-blog.csdnimg.cn/img_convert/0ba3abea3ade015ab57a6d67cd3ca95e.png)


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



![](https://img-blog.csdnimg.cn/img_convert/89455d45c85f2f2f7ba96841c557c510.png)


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



![](https://img-blog.csdnimg.cn/img_convert/8a937508d90326cc75653a5496a84bf6.png)


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



![](https://img-blog.csdnimg.cn/img_convert/8455e41a393f64dc525718f3ddb2f04c.png)


* Environment:选择函数调用环境,Local表示本地调用,Remote表示远程调用(需要先将函数部署到AGC云端)。
* Cloud Function:选择需要触发的云函数。
* Event:输入事件参数,内容为JSON格式请求体数据。
* 单击Trigger按钮,触发执行云函数,执行结果展示在Result框内,Run面板同时打印运行日志。



![](https://img-blog.csdnimg.cn/img_convert/9aaee48e24a932037e71c8db61d7b565.png)



![](https://img-blog.csdnimg.cn/img_convert/dd77a09f1dd15e745832d1854de75e95.png)


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


* 右键单击需要部署的函数目录,选择“Deploy Function”。
* 底部状态栏右侧将显示函数打包与部署进度,直至出现“Deploy successfully”消息表示函数部署成功。



![](https://img-blog.csdnimg.cn/img_convert/feb7e09b55c3a34458f618cb3f95e696.png)


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



![](https://img-blog.csdnimg.cn/img_convert/8069d15e5c67b75cf85b4462b8b1bd2a.png)


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



![](https://img-blog.csdnimg.cn/img_convert/8607bed8af85adf5fdc5c586abcb34cc.png)


### 5.3 查看触发器标识


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



![](https://img-blog.csdnimg.cn/img_convert/06c574c79b61409f1d16a474902035ed.png)


### 5.4 调用云函数


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



![](https://img-blog.csdnimg.cn/img_convert/1010f0258257a9bd61810f4765c62fb1.png)


* 在端侧应用(Application)中“entry > src/main/ets > services”目录创建`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)});
})
});
}
复制


* 在端侧应用(Application)中“entry > src/main/ets > pages > Index.ets”文件中增加请求云函数方法。



Button(‘请求自定义云函数’)
.fontSize(16)
.onClick(() => {
getSudokuPuzzle(getContext(this)).then((ret) => {
Log.info(Constants.LOG_TAG_NAME, 单击按钮调用云函数返回结果: ${JSON.stringify(ret)})
})
})
复制


* 运行端侧(Application)应用程序,单击“请求自定义云函数”按钮,在Log控制台过滤日志查看返回结果。



![](https://img-blog.csdnimg.cn/img_convert/6ead643e4a81059b018c1aaf3fa61d7c.png)


### 5.5 实现云函数返回9\*9宫格二位数组的数独谜题和所有解


使用回溯算法填充数独谜题,并随机移除一些数字将其作为数独谜题,然后求解指定数独谜题的所有解。



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 };
复制


## 6 认证服务-邮箱认证


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


### 6.1 前提条件


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


### 6.2 扩展邮箱认证



![](https://img-blog.csdnimg.cn/img_convert/734c24409439153e7ce8e316a690a631.png)


* 调用`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 {
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);
    });
});

}
复制


### 6.3 构建邮箱登录页面


通过容器组件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)
复制



![](https://img-blog.csdnimg.cn/img_convert/63a144b3e69d18f0402fe9ba9bc80dd2.png)


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


调用自定义的登录接口实现登录,并使用首选项自定义工具接口将用户信息写入缓存。



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)});
})
}
复制


### 6.5 登录成功后跳转设置昵称头像界面


右键单击“entry > src/main/ets > pages”目录选择“New > Pages”新建`Setting`设置页面。



![](https://img-blog.csdnimg.cn/img_convert/ecea73ce9e7c4f07aca6ae1aa7038a15.png)



![](https://img-blog.csdnimg.cn/img_convert/9b8dc2b72e0c9b141db22b1448edd73f.png)


在页面中使用容器组件Grid实现头像选择(提供可选头像6个)和使用基础组件TextInput实现昵称设置。



![](https://img-blog.csdnimg.cn/img_convert/706530ef87bd2097a9d2a2db9773b2e9.png)


头像昵称设置成功后,跳转到游戏主界面,点击“开始”按钮从云函数中获取数独谜题及对应的解,然后通过容器组件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, 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) => 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)
复制



![](https://img-blog.csdnimg.cn/img_convert/f847ae5a123eb07ff7ee46af37146e02.png)


## 7 创建闯关游戏万能卡片


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


### 7.1 拉起页面的2\*2卡片


工程在创建初会自动创建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
}
});
})
复制



![](https://img-blog.csdnimg.cn/img_convert/a6d74bddc6535b5caf78920e1296b57b.png)


ArkTS卡片提供了`postCardAction()`接口用于卡片内部和提供方应用间交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。



![](https://img-blog.csdnimg.cn/img_convert/fdde459fa88e90b7ba86b4eae12f0487.png)


接口定义:`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类型,用于触发UIAbiltiy中对应的方法,必填。 |



> 
> 2\*2万能卡片提供点击卡片进入元服务主界面。
> 
> 
> 


### 7.2 创建4\*4闯关游戏卡片


4\*4服务卡片用于在桌面玩游戏,没关通关后需要通过message事件刷新卡片内容生成新的关卡。


#### 7.2.1 创建一个ArkTS卡片


创建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”创建`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”: “44",
“supportDimensions”: [
"4
4”
]
}
复制


#### 7.2.2 卡片配置文件


卡片相关的配置主要包括FormExtensionAbility的配置和卡片的配置两部分:


* 卡片需要在module.json5配置文件中的extensionAbilityes标签下,配置FormExtensionAbility相关信息。FormExtensionAbility需要填写metadata元素信息标签,其中键名为固定字符串”ohos.extension.form“,资源为卡片的具体配置信息的索引。



{
“module”: {

“extensionAbilities”: [
{
“name”: “EntryFormAbility”,
“srcEntrance”: “./ets/entryformability/EntryFormAbility.ts”,
“label”: “ s t r i n g : E n t r y F o r m A b i l i t y l a b e l " , " d e s c r i p t i o n " : " string:EntryFormAbility_label", "description": " string:EntryFormAbilitylabel","description":"string:EntryFormAbility_desc”,
“type”: “form”,
“metadata”: [
{
“name”: “ohos.extension.form”,
“resource”: “$profile:form_config”
}
]
}
]
}
}
复制


* 卡片的具体配置信息。在上述FormExtensionAbility的元信息("metadata"配置项)中,可以指定卡片具体配置信息的索引资源。如当resource指定为`$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数组标签。 | 对象 | 可缺省,缺省值为空。 |


#### 7.2.3 通过message事件刷新卡片内容


在卡片页面可以通过`postCardAction`接口触发message事件拉起FormExtensionAbility,然后由FormExtensionAbility刷新卡片内容。


* 在页面中通过注册Button的onClick点击事件互调,并在回调中调用`postCardAction`接口触发事件至FormExtensionAbility。



Image($r(‘app.media.game_start’))
.width(120).height(54)
.onClick(() => {
postCardAction(this, {
‘action’: ‘message’,
‘params’: {
‘functionName’: ‘getSudoPuzzle’
}
})
})
复制


* 在FormExtensionAbility的onFormEvent生命周期调用updateForm接口刷新卡片,通过`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> = [];
@LocalStorageProp(“answers”) answers: Array<Array<Array>> = [];
复制



![](https://img-blog.csdnimg.cn/img_convert/1ed1d47aa25a8411300a43a54363fe57.png)



![](https://img-blog.csdnimg.cn/img_convert/2dc2949bde6b4b2ca0f3c5baec05763d.png)


#### 7.2.4 卡片不支持TextInput的替代方案


ArkTS卡片具备JS卡片的全量能力,并且新增了动效能力和自定义绘制的能力,支持声明式的部分组件、事件、动效、数据管理、状态管理能力。在数独游戏中需要使用输入框录入谜题解,而ArkTS卡片暂时不具备TextInput组件能力,因此使用点击空白区域与数字按钮互换的方式替代TextInput组件能力。


* 点击空白区域记录宫格坐标。



Row() {}
.backgroundColor(0xD1D3D5)
.width(this.FULL_HEIGHT_PERCENT)
.height(this.FULL_HEIGHT_PERCENT)
.onClick(() => {
this.selectArr = [i, j];

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值