前言
eTS发布有段时间了,用它写UI不光是代码易读性,还是代码量都是相当优秀。用过以后发现再也不想用java写UI了。前段时间尝试调试了碰一碰配网,使用的是碰一碰(个人体验版)的。正式版的需要企业账号才可以,使用的是java+js。今天来尝试下eTS开发,但是ArkUI只有在API7上才能支持,目前绝大多数手机还都是API6,所以配网部分只能先代码模拟测试,界面效果如下图:
项目分为两个模块,碰一碰配网entry模块,设备控制control模块。不多说了上代码。
entry模块
这个模块主要是实现设备联网。界面很简单。
UI界面
配网界面就是3个组件,一个图片2个text。
- build() {
- Row(){
- Column() {
- Image($r('app.media.test'))
- .width(152)
- .height(152)
- .margin({ top: 16 })
- Text(this.desc)
- .fontSize(14)
- .fontColor('#FF0000000')
- .margin({ top: 16 })
- Text(this.progress)
- .fontSize(14)
- .fontColor('#999999')
- .margin({ top: 2,right: 24,bottom: 20,left: 24 })
- }
- .height('35%')
- .width('80%')
- .margin('10%')
- .borderRadius(10)
- .backgroundColor(0xFFFFFF)
- }
- .width('100%')
- .height('100%')
- .alignItems(VerticalAlign.Bottom) //这是Row的参数
- .backgroundColor(0x000000)
- }
配网流程
整个配网的流程就是手机碰触NFC贴纸,获取Product ID,然后通过Product ID去云端获取用户意图。说白了就是告诉手机要打开哪个包名,哪个模块。关于如何在华为开发者门户设置用户意图可以看我之前的帖子。这里就是启动了entry模块。
接下来分析用eTS如何实现这个过程:
1.导入hilink包
在build.gradle中进行配置,底层的实现都是通过调用API,并不需要自己写。
- dependencies {
- ... ...
- implementation(group: 'com.huawei.hilink', name: 'ailifeability', version: '1.0.0.1', ext: 'har')
- }
2.调用PA能力。
使用JSCallJava调用API接口,这个可以参考OneHop模板,来实现一个default/common/fa-netconfig.ets它的作用就是将JAVA API转换为eTS函数接口,主要用到的就是FeatureAbility.callAbility(action)。
- function callAbilityFunc(callCode, argsObj, callbackFunc) {
- let action = { // 要调用java的信息存放在action
- bundleName : CONSTANT.BUNDLE_NAME,
- abilityName : CONSTANT.ABILITY_NAME,
- messageCode : callCode, // callCode用来区分要调用哪一个API
- abilityType : 1,
- data : argsObj,
- };
- return FeatureAbility.callAbility(action);//调用PA能力
- }
3.eTS生命周期回调函数
要在entry被拉起时自动执行配网,就要使用.eTS生命周期回调函数。
由于本篇主题是ArkUI,至于配网流程等专门写一篇帖子再来分析。
- aboutToAppear(){
- this.discoverDevice() // 执行配网流程
- }
control模块
接下来和大家分享下在制作control模块时,学习到的ArkUI知识点和踩到的一些坑。其中部分内容官方文档也没有写,都是参考示例代码连蒙带猜。
1.设置窗口模式
在OneHop官方模板src/main/java/com/liangzili/myonehop/MainAbility.java下给window_modal设置了一个参数。在官方文档中好像没有关于这个参数的说明,也或许是我没有找到。
- public void onStart(Intent intent) {
- intent.setParam("window_modal", 3);
- ... ...
- }
测试发现这个参数可以很方便的实现类似弹窗下拉这样的效果,省去了很多界面代码。其中("window_modal", 3)就是配网entry页面的效果,("window_modal", 1)的效果可以看下图。而且比较有意思的是有1有3,但是传递2好像没啥效果。不过可惜的是,JS范式下传递这个参数效果如下图,但eTS下会有bug,要不就是弹窗无法拖拽,要不就是全屏无法设置大小。
2.app在线设计
在官方指导中有提到HiLink可以使用在线可视化的方式设置界面,效果如下图。
看着就很方便,在线设计完成之后会得到一个界面文件。类似下图这样的效果。界面的数据要统一放到了src/main/resources/rawfile下,根据productName参数进行区选择,格式为JSON。(PS:本想体验下这个app在线设计,但是打开没有内容,我只好使用的模板里带的FAN_zh.json)
control模块需要解析json文件的数据来绘制界面。
3.配置文件的解析
配置文件的解析也是java来完成的,所以我直接原文复制的OneHop模板的java代码,eTS部分,在src/main/ets/default/pages/index.ets下。
- async onPageShow(){
- utils.setActionParam('com.liangzili.myonehop', // 为action初始化参数
- 'com.liangzili.myonehop.DataHandlerAbility', ABILITY_TYPE_INTERNAL)
- await this.requestTemplate()
- }
- .. ...
- async requestTemplate() {
- let action = utils.makeAction(ACTION_MESSAGE_CODE_GET_TEMPLATE, {});
- let result = await FeatureAbility.callAbility(action); // 同样的方法去调用PA的能力
- let resultJson = JSON.parse(result); // 返回的结果保存在 result 中
- if (resultJson.code == 0) { // 不等于0就调用失败了,可以通过失败码查找问题
- let template = JSON.parse(resultJson.data);
- this.parseJson(template.template);
- }
- }
- async parseJson(deviceInfo) { ... ... } // 最后就是解析JSON来生成界面了
4.生成界面
其实如果用eTS范式单纯生成一个右侧这样的界面,可能只需要几十行代码。但是要解析在线生成的JSON界面文件,再兼容各种样式的控制界面来绘制UI,这个问题就会变得复杂的多。可能官方的意思是想通过在线设计降低程序的工作量,或者是为了统一UI风格,降低用户学习成本。
index.ets
主界面分为两个区域,DeviceInfo() 和 Control(),DeviceInfo就一个主图和名字。
- build() {
- Stack({ alignContent: Alignment.Bottom }){ // 用来模拟一个上边圆角的效果
- Column(){}.height(35).width('100%').backgroundColor(0xF6F6F6) // 用来覆盖下端边框圆角
- Column() {
- DeviceInfo() // 设备信息组件
- Control() // 控制面板组件
- }
- .height('95%')
- .width('100%')
- .borderRadius(25)
- .backgroundColor(0xF6F6F6)
- }
- .width('100%')
- .height('100%')
- .backgroundColor(0x000000)
- }
Control.ets
界面中通过传递参数,来实现用一个组件,显示不同内容。
- build(){
- Column(){
- Reversal(this.reversalData1) //开关组件
- Enum({ enumDatas: this.enumDatas1 }) //枚举组件
- Enum({ enumDatas: this.enumDatas2 }) //枚举组件
- Reversal(this.reversalData2) //开关组件
- }
- }
5.参数传递
组件间传值是我遇到问题比较大的地方,我总结了以下几种情况。这些基本能解决大部分的传值问题了。
1.单个变量
- // 调用端
- Component1({a01:"a01"}) // 调用的时候参数用{}包裹
- // 被调用端
- @Component
- export struct Component1{
- private a01:string // 这里定义变量,用来接收
- build(){
- Text(this.a01).fontSize(50)
- }
- }
2.对象键值对
- // 调用端
- struct Index {
- parameter:{} = {b01: "b01",b02: "b02",}
- build() {
- Column(){
- Component2(this.parameter) // 调用的时候参数不用{}包裹
- }
- }
- }
- // 被调用端
- @Component
- export struct Component2{
- private b01:string // 这里定义变量,用来接收
- private b02:string
- build(){
- Column(){
- Text(this.b01).fontSize(50)
- Text(this.b02).fontSize(50)
- }
- }
- }
3.对象数组
- // 调用端
- struct Index {
- parameters:any[] = [
- {
- c01:"c01",
- c02:"c02"
- },
- {
- c01:"c11",
- c02:"c22"
- }
- ]
- build() {
- Column(){
- Component3({ parameters: this.parameters }) // 传递参数是parameters,对象数组类型
- }
- }
- }
- // 被调用端
- @Component
- export struct Component3{
- private parameters:any[] // 定义同样的参数,同样的类型
- build(){
- Column(){
- ForEach(this.parameters,(item:any) => { //【重要:获取数组之后可以直接使用ForEach遍历数据】
- Text(item.c01).fontSize(50)
- Text(item.c02).fontSize(50)
- },(item:any) => item.toString() // 文档说选填,但不填会失败
- )
- }
- }
- }
4.自定义类数组
- // 类
- class enumData {
- image: Resource
- text: string
- constructor(image: Resource, text: string) {
- this.image = image;
- this.text = text;
- }
- }
- // 调用端
- @Entry
- @Component
- struct Index {
- enumDatas:enumData[] = this.getenumDatas()
- getenumDatas(){
- let enumDatas: Array<enumData> = []
- enumDatas.push(new enumData($r("app.media.icon"), "001"))
- enumDatas.push(new enumData($r("app.media.icon"), "002"))
- return enumDatas;
- }
- build() {
- Column(){
- Component4({ enumDatas: this.enumDatas })
- }
- }
- }
- // 被调用端
- @Component
- export struct Component4{
- private enumDatas:enumData[] // 这里定义变量,用来接收
- build(){
- Column(){
- ForEach(this.enumDatas,(item:any) => {
- Image(item.image).width(50).height(50)
- Text(item.text).fontSize(50)
- },(item:any) => item.text.toString() // 文档说选填,但不填会失败
- )
- }
- }
- }
6.发送设备控制信息
由于目前没有API7的真机进行调试,所以发送控制设备信息这部分还没有实现。但是以防万一控制流程先记录下来,方便以后再来添加。
与entry模块类似,需要在build.gradle中进行配置,同样的API。
- dependencies {
- ... ...
- implementation(group: 'com.huawei.hilink', name: 'ailifeability', version: '1.0.0.1', ext: 'har')
- }
eTS侧要实现这样的函数,来调用PA的能力。
- async setKeyValue(key, value) {
- let data = {};
- data[key] = value;
- let action = utils.makeAction(ACTION_MESSAGE_CODE_DATACHANGED, data);
- let that = this;
- that.data.timer = setTimeout(function () {
- that.notifyObservers('showMessage', {
- 'show': true
- });
- }, WAIT_TIME);
- await FeatureAbility.callAbility(action);
- }
src/main/java/com/liangzili/myonehop/DataHandlerAbility.java
java侧根据ACTION_MESSAGE_CODE_DATA_CHANGED来确定要调用的方法。
- case ACTION_MESSAGE_CODE_DATA_CHANGED: {
- String zsonStr = data.readString();
- ZSONObject zsonObj = ZSONObject.stringToZSON(zsonStr);
- for (Map.Entry<String, Object> entry : zsonObj.entrySet()) {
- deviceDataHandler.modifyDeviceProperty(entry.getKey(), entry.getValue());
- }
- break;
- }
以上就是我在使用ArkUI开发时,一些重要知识点的总结,希望对朋友们有所帮助。