鸿蒙元服务开发实例:桌面卡片上的电动自行车助手E-Bike

一、元服务E-Bike简介

E-Bike是一款基于HarmonyOS开发的元服务,以万能卡片的形式给骑行提供便捷服务,主要功能包括:

  1. 车辆状态信息获取:用户可在元服务内连接电动自行车(真机和自行车自备),查看车辆位置、剩余电量、续航里程以及累计骑行里程。

  2. 包括响铃找车功能:按钮可触发车辆鸣响,便于快速确认车辆具体位置(真机和自行车自备)。

  3. 用户可通过右上角按钮添加2x2或2x4卡片,在桌面可直接查看车辆状态信息。

二、环境搭建

首先需要完成HarmonyOS开发环境搭建。E-Bike是元服务,且为端云一体化开发模式,新建工程可可参照如图步骤进行(注意该模式下APP为Stage模型)。  

软件要求

DevEco Studio版本:DevEco Studio 3.1 Release及以上版本。

HarmonyOS SDK版本:API version 9及以上版本。

硬件要求
  • 设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。
  • HarmonyOS系统:3.1.0 Developer Release及以上版本。
  • 电动自行车(获取真实车辆数据,车辆为作者自制)
环境搭建
  1. 安装DevEco Studio,详情请参考下载和安装软件

  2. 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

    • 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。

    • 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境

  3. 开发者可以参考以下链接,完成设备调试的相关配置:

    使用真机进行调试

    使用模拟器进行调试

三、代码结构解读

本篇教程只对E-Bike实现的核心代码进行讲解,对于完整代码,会在源码下载或gitee中提供。主要的程序框架如下:

entry\src\main\ets

│ ├─common--通用常量和数据

│ ├─entryability -- EntryAbility.ts 程序入口

│ ├─entryformability--EntryFormAbility.ts卡片入口

│ ├─pages---Index.ts 应用主页

│ ├─services

│ ├─widget

│ │ └─pages---2x2 ArkTS卡片

│ └─widget24

│ └─pages---2x4卡片

└─resources ---资源文件目录

四、应用主页面UI和功能开发
4.1 主页面UI

新建工程后,在entry\src\main\ets\pages\Index.ts文件中已有一个模板案例,我们需要删除其中的代码,然后构建自己的页面。具体实现方法是:

  1. 删除build() { }中的代码。

  2. 使用Column、Flex、Row容器和Button、Image、Text组件构建E-Bike布局。

  3. 在UI中加入逻辑判断具体要显示的UI组件。如响铃找车的Image组件内容由用户的点击状态决定,点击响铃找车则Image切换为响铃状态,反之亦然。

具体代码实现框架如下:

@Entry @Component struct Index {
    build() { Column({space:10})
    {
        // 背景图
        Image($r("app.media.Ebike"))
            ···
        Flex()
        {
            // 响铃找车
            Column()
            {
                if(this.display_flag==1)
                {
                Image($r("app.media.ic_ring_on_filled"))
                .height("55%")
                .objectFit(ImageFit.Contain)
                .interpolation(ImageInterpolation.High)
                .onClick(() => { this.display_flag +=1; if(this.display_flag>2)
                    {
                    this.display_flag =1;
                    }
                })
            }
            if(this.display_flag==2)
            {
            Image($r("app.media.ic_ring_off_filled"))
            .height("55%")
            .objectFit(ImageFit.Contain)
            .interpolation(ImageInterpolation.High)
            .onClick(() => { this.display_flag+=1; if(this.display_flag>2)
                    {
                    this.display_flag =1;
                    }
                })
            }
            Text("响铃找车")
            ···
        }
        // 获取定位
        Column()
        {
        Image($r("app.media.ic_statusbar_gps"))
        ····
        })
        Text(this.bike_location)
        ····
        }
        ····
        }.width("95%").height("10%")
        // 电量 设置

        Flex({ direction: FlexDirection.Row,justifyContent: FlexAlign.SpaceBetween,})
        {
        // 电量
        Column() { Row()
        {
        Image($r("app.media.ic_power"))
        ···
        // 电量值
        Text(this.bike_power.toString() + '%')
        ···
        }
        Text("剩余电量")
        ···
        }
        ···
        // 设置
        Column() { Image($r("app.media.ic_public_settings_filled"))
        ···
        Text("车辆设置")
        ···
        }···
        }

        // 骑行数据
        Flex({ direction: FlexDirection.Column})
        {
        Row()
        {
        Text(" 累计骑行")
        ··· Blank()
        Text("预计续航 ")
        ···
        }
        }
        }
    }
}
复制
4.2 主页功能开发

主页主要实现的功能包括:连接车辆、获取需要展示的数据(车辆电量、位置、里程数据等)、将数据持久化便于页面展示。 车辆与E-Bike通过Socket TCP协议方式连接。鉴于此部分依赖硬件,这里主要介绍E-Bike应用层如何开发 Socket通信。实现如下: Index.ets:

// 1. 首先要引入模块,创建TCPSocket对象
import socket from '@ohos.net.socket'
// 创建一个TCPSocket连接,返回一个TCPSocket对象。
let tcp = socket.constructTCPSocketInstance();
tcpInit() {
    // 2. 绑定IP地址和端口。
    let bindAddress = { address: '192.168.xx.xx',port: 1234,// 绑定端口,如1234
    family: 1
    };
    tcp.bind(bindAddress,err => { if (err) {
    console.log('bind fail'); return;
    }
    console.log('bind success');
    }
    // 3.其次是订阅TCPSocket相关的订阅事件,如on message,有数据传入 
    tcp.on('connect',() => {
    this.tcp_status = '连接ok' 
        prompt.showToast({message:this.tcp_status})
    });
    tcp.on('message',value => {
    this.message_recv = this.resolveArrayBuffer(value.message) 
        prompt.showToast({message:this.message_recv})
    });
    tcp.on('close',() => { 
        prompt.showToast({message:"tcp close"})
    });
}
// 4.连接到指定的IP地址和端口。 tcpConnect() {
tcp.getState().then((data) => {
        if (data.isClose) { this.tcpInit()
        }
        // 连接
        tcp.connect(
        {address: { address: this.GetSetIP,port: this.localAddr.port,family: 1},timeout: 2000}
        ).then(() => {
            prompt.showToast({message:" tcp connect successful"})
        }).catch((error) => { prompt.showToast({message:"tcp connect failed"})
     })
  })
}
// 5.发送数据 
tcpSend() {
tcp.getState().then((data) => 
  { if (data.isConnected) {
    // 发送消息

    tcp.send(
    { data: this.message_send,}
    ).then(() => {
    prompt.showToast({message:"send message successful"})
    }).catch((error) => { prompt.showToast({message:"send failed"})
    })
    } else {
    prompt.showToast({message:"tcp not connect"})
  }
 })
}
// 6. TCP连接操作界面通过设置按钮控制,点击设置按钮即可弹出,可输入车辆IP地址,确认连接
if(this.show_setting==1)
{
    Flex({ direction: FlexDirection.Row})
    {
        Row()
        {
            Text('车辆IP')
            TextInput({ placeholder: '192.168.43.164'})
            .onChange((value: string) => { this.InputIP = value
            })
            Button('连接').onClick(()=>
            {
                    this.GetSetIP=this.InputIP; 
                 this.tcpInit()
            })
        }
    }
}

// 7.点击组件,实现发送指令,并获取对应数据,这里以电量为例: 
Image($r("app.media.ic_power"))
        .height("80%")
        .objectFit(ImageFit.Contain)
        .onClick(() => {
        // this.bike_power = 99;
        this.message_send = MSG_CMD.GET_BIKE_POWER this.tcpSend()
        this.bike_power = this.message_recv;
    })
/ 8.连接使用完毕后,主动关闭。取消相关事件的订阅。 
setTimeout(() => {
    tcp.close((err) => { console.log('close socket.')
    });
    tcp.off('message');
    tcp.off('connect');
    tcp.off('close');
},30 * 1000);
复制
五、卡片开发
5.1 卡片UI开发

开发卡片的目的是将自行车的数据展示在桌面上,让用户一目了然。用默认模板创建工程时,自动创建了一张卡片,在form_config.json文件改动配置为自动刷新,支持2x2.

//form_config.json
"colorMode": "auto","isDefault": true,"updateEnabled": false,"scheduledUpdateTime": "10:30","updateDuration": 1,"defaultDimension": "2*2","supportDimensions": [
"2*2"
]
复制

为展示电量信息,且布局不同,由此需要创建一张2x4的卡片。操作如下:在main目录下,点击鼠标右键 > New > Service Widget。

然后选择Hello World卡片模板,点击Next。

填写卡片名字(如Widget24Card)、开发语言(ArkTS和JS可选)、支持卡片规格(Support dimension 2x4)、关联表单(Ability name)点击Finish完成创建。修改配置为自动刷新,支持2x4。 创建完卡片后,在ets文件目录下显示卡片目录,然后开发者使用ArkTS开发卡片页面。效果如图所示: 

为两张卡片开发UI,resource资源共用。使用Flex容器开发卡片,保证不同屏幕大小的显示效果。同时为组件绑定事件,用户可以主动获取数据,具体UI布局代码不再赘述,实现2x2和2x4效果如下

5.2 卡片数据刷新

通过message事件刷新卡片内容,在卡片页面中可以通过postCardAction接口触发message事件拉起FormExtensionAbility,然后由FormExtensionAbility刷新卡片内容。下面是这种刷新方式更新电量的简单示例。在卡片页面通过注册电量图标Image组件的onClick点击事件回调,并在回调中调用postCardAction接口触发事件至FormExtensionAbility。

// Widget24Card.ets:
let storage = new LocalStorage();
@Entry(storage) @Component
struct WidgetCard24 {
    ···
    @LocalStorageProp('bike_power') bike_power: number = 50;
    ··· 
    build() {
        Row({space:5}) {
            // 背景图 电量
            Column()
            {
                Row()
                {
                    Image($r("app.media.ic_power"))
                    ···
                    .onClick(() => { postCardAction(this,{
                    'action': 'message','params': { 'bike_power': 55
                    }
                    });
                    })
                    // 电量值
                Text(`${this.bike_power}`+'%')//this.bike_power.toString()+'%')
                ···
            }
            ···
        }
    }
}

// EntryFormAbility.ets:
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; import formProvider from '@ohos.app.form.formProvider';
export default class EntryFormAbility extends FormExtensionAbility {

find_bike: string = "app.media.ic_ring_off" bike_power: number = 55.6
display_flag : number = 1 
    bike_location: string = "长安街1号" 
    bike_distance: number = 520
    bike_duration: number = 479 
    my_font_size : number = 12

formData = {
    'title': this.find_bike,'bike_power': this.bike_power,'bike_distance':this.bike_distance,'bike_duration':this.bike_duration,'bike_location':this.bike_location,'detail': 'Detail Update Success.',// 和卡片布局中对应
}

onFormEvent(formId,message) {
        console.info(`FormAbility onEvent,formId = ${formId},message:
    ${JSON.stringify(message)}`);
    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));
        })
    }
    ...
}
复制

实现效果如下图: 

参考链接:元服务官网

 

点击进入华为官网

原文地址:https://blog.csdn.net/csdndevpress0028/article/details/132625184

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读1.4k次。被@Observed装饰的类,可以被观察到属性的变化;子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中是属性,这个属性同样也需要被@Observed装饰。单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。_鸿蒙ark对象数组
文章浏览阅读1k次。Harmony OS_harmonyos创建数据库
文章浏览阅读1.1k次,点赞25次,收藏23次。自定义组件Header.ets页面(子组件)//自定义组件@Component//组件声明private title:ResourceStr//接收的参数build(){Row() {index.ets(父组件)//导入自定义组件@Entry@Componentbuild() {Column() {/*** 1. 自定义组件调用-----自定义组件------* 2. 在调用的组件上设置样式*/ShopTitle({ title: '商品列表' })
文章浏览阅读952次,点赞11次,收藏25次。ArkUI是一套构建分布式应用界面的声明式UI开发框架。它使用极简的UI信息语法、丰富的UI组件、以及实时界面预览工具,帮助您提升移动应用界面开发效率30%。您只需使用一套ArkTS API,就能在Android、iOS、鸿蒙多个平台上提供生动而流畅的用户界面体验。_支持ios 安卓 鸿蒙next的跨平台方案
文章浏览阅读735次。​错误: 找不到符号符号: 变量 Layout_list_item位置: 类 ResourceTable_错误: 找不到符号 符号: 变量 resourcetable 位置: 类 mainabilityslice
文章浏览阅读941次,点赞23次,收藏21次。harmony ARKTS base64 加解密_鸿蒙 鸿蒙加解密算法库
文章浏览阅读860次,点赞21次,收藏24次。使用自定义布局,实现子组件自动换行功能。图1自定义布局的使用效果创建自定义布局的类,并继承ComponentContainer,添加构造方法。//如需支持xml创建自定义布局,必须添加该构造方法实现ComponentContainer.EstimateSizeListener接口,在onEstimateSize方法中进行测量。......@Override//通知子组件进行测量//关联子组件的索引与其布局数据idx++) {//测量自身。_鸿蒙javaui
文章浏览阅读917次,点赞25次,收藏25次。这里需要注意的是,真机需要使用华为侧提供的测试机,测试机中会安装纯鸿蒙的系统镜像,能够体验到完整的鸿蒙系统功能,纯鸿蒙应用目前还不能完美地在 HarmonyOS 4.0 的商用机侧跑起来。当前,真机调试需要使用华为侧提供的测试机,测试机中会安装纯鸿蒙的系统镜像,能够体验到完整的鸿蒙系统功能,纯鸿蒙应用目前还不能完美地在 HarmonyOS 4.0 的商用机侧跑起来。另外,由于样式的解析是基于组件文件的纬度的,因此样式文件只能应用于被其引用的组件文件中,而不能跨文件应用,并且样式文件也只支持类选择器。_鸿蒙 小程序
文章浏览阅读876次,点赞17次,收藏4次。2. HarmonyOS应用开发DevEcoStudio准备-1HUAWEI DevEco Studio为运行在HarmonyOS和OpenHarmony系统上的应用和服务(以下简称应用/服务)提供一站式的开发平台。
文章浏览阅读811次。此对象主要映射JSON数组数据,比如服务器传的数据是这样的。_arkts json
文章浏览阅读429次。鸿蒙小游戏-数字华容道_华为鸿蒙手机自带小游戏
文章浏览阅读1.1k次,点赞24次,收藏19次。Ability是应用/服务所具备的能力的抽象,一个Module可以包含一个或多个Ability。
文章浏览阅读846次。本文带大家使用MQTT协议连接华为IoT平台,使用的是E53_IA1 智慧农业扩展板与 BearPi-HM_Nano 开发主板_mqtt 如何对接第三方iot平台
文章浏览阅读567次。HarmonyOS_arkts卡片
文章浏览阅读1k次,点赞19次,收藏20次。ArkTS开发鸿蒙OS连接mongoDB(后端node.js)2024最新教程
文章浏览阅读1.2k次,点赞23次,收藏15次。HarmonyOS与OpenHarmony(1)本质上的不同是:HarmonyOS是鸿蒙操作系统,而OpenHarmony则是从开源项目。这里可以联想一下Android,比如小米手机在Android开源系统的基础上开发了MIUI的手机操作系统,HarmonyOS就类似于MIUI,OpenHarmony类似Android基础底座。(2)HarmonyOS:是双框架,内聚了AOSP(Android Open Source Project )和OpenHarmony等。_鸿蒙模拟器开了怎么跑代码
文章浏览阅读1.1k次,点赞21次,收藏21次。鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Navigation组件。
文章浏览阅读2k次。由于之前的哥们匆忙离职了,所以鸿蒙手表项目的新版本我临时接过来打包发布,基本上之前没有啥鸿蒙经验,但是一直是做Android开发的,在工作人员的指导下发现打包配置基本上和Android一样,所以这些都不是问题,这里记录一下使用过程中遇到的问题。!过程和遇到的问题基本上都讲解了,关机睡觉,打卡收工。_鸿蒙系统adb命令
文章浏览阅读7.3k次,点赞9次,收藏29次。39. 【多选题】_column和row容器中,设置子组件在主轴方向上的对齐格式
文章浏览阅读1.1k次,点赞13次,收藏24次。18.鸿蒙HarmonyOS App(JAVA)日期选择器-时间选择器点击button按钮显示月份与获取的时间。_harmonyos农历获取