用ArkUI实现碰一碰配网、设备控制 原创 精华

丨张明亮丨
发布于 2021-12-27 15:27
浏览
10收藏

【本文正在参与优质创作者激励】

前言

eTS发布有段时间了,用它写UI不光是代码易读性,还是代码量都是相当优秀。用过以后发现再也不想用java写UI了。前段时间尝试调试了碰一碰配网,使用的是碰一碰(个人体验版)的。正式版的需要企业账号才可以,使用的是java+js。今天来尝试下eTS开发,但是ArkUI只有在API7上才能支持,目前绝大多数手机还都是API6,所以配网部分只能先代码模拟测试,界面效果如下图:

用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区 用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区

项目分为两个模块,碰一碰配网entry模块,设备控制control模块。不多说了上代码。

用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区

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生命周期回调函数。

函数名 描述
aboutToAppear 函数在创建自定义组件的新实例后,在执行其build函数之前执行。允许在aboutToAppear函数中改变状态变量,这些更改将在后续执行build函数中生效。
aboutToDisappear 函数在自定义组件析构消耗之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是**@Link**变量的修改可能会导致应用程序行为不稳定。
onPageShow 当此页面显示时触发一次。包括路由过程、应用进入前后台等场景,仅@Entry修饰的自定义组件生效。
onPageHide 当此页面消失时触发一次。包括路由过程、应用进入前后台等场景,仅@Entry修饰的自定义组件生效。
onBackPress 当用户点击返回按钮时触发,,仅@Entry修饰的自定义组件生效。返回true表示页面自己处理返回逻辑, 不进行页面路由。返回false表示使用默认的返回逻辑。不返回值会作为false处理。

由于本篇主题是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,要不就是弹窗无法拖拽,要不就是全屏无法设置大小。

不传递参数 (“window_modal”, 1) (“window_modal”, 3)
界面高度:50% 界面高度:100% 界面高度:50%
界面背景:灰色 界面背景:灰色 界面背景:灰色
用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区 用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区 用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区

2.app在线设计

官方指导中有提到HiLink可以使用在线可视化的方式设置界面,效果如下图。

用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区

看着就很方便,在线设计完成之后会得到一个界面文件。类似下图这样的效果。界面的数据要统一放到了src/main/resources/rawfile下,根据productName参数进行区选择,格式为JSON。(PS:本想体验下这个app在线设计,但是打开没有内容,我只好使用的模板里带的FAN_zh.json)用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区

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风格,降低用户学习成本。

用ArkUI实现碰一碰配网、设备控制-鸿蒙开发者社区

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开发时,一些重要知识点的总结,希望对朋友们有所帮助。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
11
收藏 10
回复
举报
8条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

很全面的教程!

回复
2021-12-27 16:49:59
Der_带鱼
Der_带鱼

直接码住!感谢亮哥

2
回复
2021-12-27 23:31:02
丨张明亮丨
丨张明亮丨 回复了 Der_带鱼
直接码住!感谢亮哥

客气了,互相学习啊

回复
2021-12-29 14:59:43
时空未宇
时空未宇

重磅帖子,赞👍

回复
2021-12-29 17:41:47
raulzhou
raulzhou

啥时候用js开发一个教程呀,TS不会呀

1
回复
2022-1-14 11:07:30
Hello_Kun
Hello_Kun 回复了 raulzhou
啥时候用js开发一个教程呀,TS不会呀

我最近在搞这个

回复
2022-5-13 16:07:44
raulzhou
raulzhou 回复了 Hello_Kun
我最近在搞这个

看了你的帖子,你偏重的设备端的,我是做应用端(前端),嵌入式的看不懂。。

回复
2022-5-19 09:24:33
Hello_Kun
Hello_Kun 回复了 raulzhou
看了你的帖子,你偏重的设备端的,我是做应用端(前端),嵌入式的看不懂。。

哈哈,我是从app到设备端都做了解释,也有前端js那部分的。https://ost.51cto.com/posts/12758;https://ost.51cto.com/posts/12772

 

回复
2022-5-19 09:29:19
回复
    相关推荐