一, 前言
自从HarmonyOS发布以来, 原子化服务卡片成为亮点中亮点, 我平常上班交通工具都是公交车多, 平常在出门前,下班前都会打开微信小程序[车来了精准实时公交]查看要坐的公交现在什么位置, 每次都要先打开微信, 找到小程序,才可以查看公交行程情况, 有些麻烦, 这时HarmonyOS原子化服务卡片出现了, 我就想能不能把某路公交车行程直接显示在卡片上,这样就不用每次都要先打开微信,再找到小程序查看, 有了想法当然就是运动了,虽然我拿不到公交车行程信息, 但是我可以模拟,把固定几路公交车信息录入数据库,还有某路公交经过的站牌也录入到数据. 有了模拟数据, 一切都好办了, 我们就开始动手吧.
二, 实现效果
每个服务卡片绑定一路公交车, 设置好候车站, 每5秒更新公交车前进一个站, 当公交车到达候车站时, 公交车前进进度条停止. 除了显示公交车前进状态, 也显示出前所在位置站牌.
编辑卡片-不同规格卡片绑定不同的公交车线路: https://v.youku.com/v_show/id_XNTE4MjAzOTE1Ng==.html?x=&sharefrom=android&sharekey=c7aa0569d2c0d621414c23f1dccb240d0
手机版视频: https://v.youku.com/v_show/id_XNTE3OTg0NTU4OA==.html?x=&sharefrom=android&sharekey=4e0cc9e1d7f9bad69a246653533d868a5
平板版视频: https://v.youku.com/v_show/id_XNTE3OTg0NjAwOA==.html?x=&sharefrom=android&sharekey=26bcd9a03f8676f652f40de56bd2322b1
三, 创建工程
在这当作你已经安装好最新版本DevEco-Studio开发工具, 点击File -> New -> New Project... 弹出Create HarmonyOS Project窗口, 这里我选择空白JS模板创建, 写界面还是JS比较方便些, 对于有一定前端知识的小伙伴来说, 创建JS项目的操作我就不截图, 那就操作两个界面.
四, 生成服务卡片
在左边树形目录entry 右击New -> Service Widget

给卡片起个靓名字, 同时选择创建哪些规格卡片,既然是原子化服务卡片, 当然是全部选择了.

点击完成, 在Java包下自动生成一个widget的包, 该包自动创建卡片相关代码, 里面使用的是工厂模式最通用的单例模式, 同时在JS目录下自动生成一个widgetBus目录, 里面包含服务卡片界面相关代码.
五, 主界面开发
虽然本贴子主要介绍是服务卡片, 但要服务卡片有丰富的内容显示和动画, 还得有后台的大力支持, 比如Data Ability保存卡片需要数据, Service Ability 动态显示公交车前进动画进度条,这里先介绍一下整个项目的结构:

主界面效果:


hml代码以下:
这里就只贴hml代码, JS和CSS可以到gitee上查看源代码.
车站牌效果: 手机版可以左右滑动


hml代码以下:
这里就只贴hml代码, JS和CSS可以到gitee上查看源代码.
六, 终于到主角登场了
原子化服务卡片来了, 不同规格卡片,不同设备显示,都是同一套代码




hml代码以下:
这里就只贴hml代码, JS和CSS可以到gitee上查看源代码.
七, 实体类代码片段:
要使用@Entity, @PrimaryKey, @Database前, 必须在entry目录下的build.gradle文件添加红色框内容

八, 增加长按卡片编辑页面
1. 创建卡片编辑Ability(BusCardConfigAbility)
右击entry>New>Ability>Page Ability(JS)

在BusCardConfigAbility.onstart中添加setInstanceName("BusCardConfig");
2. 在config.json配置文件中增加属性:formConfigAbility
九, 编辑页面开发&编辑更新卡片逻辑开发
hml代码以下:
JS代码以下:
import app from '@system.app';
const BUTTON_STATE_IMAGE = ['/common/images/checkbox-blank.png', '/common/images/checkbox-circle.png'];
export default {
data: {
"busList": [
{"id": 1, "busNum": "987路", "waitingStation": "海珠客运站总站", "endStation": "天安科技园总站", "timeRemaining": 5, "stationsRemaining": 1, "kmRemaining": 1.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
{"id": 2, "busNum": "303路", "waitingStation": "太古仓路总站", "endStation": "海珠客运站总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
{"id": 3, "busNum": "288路", "waitingStation": "大夫山公园总站", "endStation": "洛溪新城总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
{"id": 4, "busNum": "129路", "waitingStation": "洛溪新城总站", "endStation": "奥林匹克花园总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
{"id": 5, "busNum": "230路", "waitingStation": "华工大总站", "endStation": "客村立交总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
{"id": 6, "busNum": "882路", "waitingStation": "客村立交总站", "endStation": "员村总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]}
],
"keyword": "",
"busId": -1
},
onInit() {
this.getBusList();
},
initAction: function (code, actionData) {
var action = {};
action.bundleName = "com.army.study";
action.abilityName = "com.army.study.service.BusInternalAbility";
action.messageCode = code;
action.data = actionData;
action.abilityType = 1;
action.syncOption = 0;
return action;
},
getBusList: async function () {
var actionData = {"keyword": this.keyword};
try {
var action = this.initAction(1001, actionData);
var result = await FeatureAbility.callAbility(action);
console.info(" @@result = " + JSON.stringify(result));
this.busList = JSON.parse(result);
} catch (pluginError) {
console.error("getBusList : Plugin Error = " + pluginError);
}
},
change(e) {
this.keyword = e.value;
this.getBusList();
},
bindingCard: async function () {
var actionData = {"busId": this.busId};
try {
var action = this.initAction(1003, actionData);
var result = await FeatureAbility.callAbility(action);
console.info(" @@result = " + result);
if("OK" == result) {
setTimeout(function(){app.terminate();},500);
}
} catch (pluginError) {
console.error("bindingCard : Plugin Error = " + pluginError);
}
},
checked(id) {
let _this = this;
_this.busList.forEach(element => {
element.checkBtn = element.id == id ? BUTTON_STATE_IMAGE[1] : BUTTON_STATE_IMAGE[0]
});
this.busId = id;
this.bindingCard();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
CSS代码请移步到gitee查看源码
更新卡片数据片段代码:
效果图:

其它Ability代码文件, 都有注释, 有兴趣的小伙伴可以下载源码查看, 项目还不算完整版, 下来会慢慢更新, 源码也会同步到gitee码云
源码在这: https://gitee.com/army16_harmony/bus-comes
这个应用我天天用,而且真的非常适合卡片这个场景!!
我也是上班时间用这个应用的微信小程序
这个厉害!
谢谢点赞,下来添加编辑卡片功能,也就是不同卡片绑定不同公交车。
好东西,非常实用
还可以把公交乘车码放到卡片里
那真是越来越方便了,大爱服务卡片功能了
卡片功能会给生活带来很大的方便
是的,只有想不到,没有做不到的
这个功能扩展一下,地铁,火车,打车,步行都可以一打开手机就看到自己位置
这个功能很实用,尤其是公交上面。这个很节省上班时间,能够自己安排出行问题。 收藏下~
源码已包含卡片编辑,不同卡片可以绑定不同公交车线路
这个实用方便快捷,
是的, 在桌面就可以一目了然
这个实用方便,可以进行商业化推广