[文章]#HarmonyOS征文#基于JS,我成功开发了HarmonyOS 车来了服务卡片

阅读量0
0
4


, 前言
  自从HarmonyOS发布以来, 原子化服务卡片成为亮点中亮点, 我平常上班交通工具都是公交车多, 平常在出门前,下班前都会打开微信小程序[车来了精准实时公交]查看要坐的公交现在什么位置, 每次都要先打开微信, 找到小程序,才可以查看公交行程情况, 有些麻烦, 这时HarmonyOS原子化服务卡片出现了, 我就想能不能把某路公交车行程直接显示在卡片上,这样就不用每次都要先打开微信,再找到小程序查看, 有了想法当然就是运动了,虽然我拿不到公交车行程信息, 但是我可以模拟,把固定几路公交车信息录入数据库,还有某路公交经过的站牌也录入到数据. 有了模拟数据, 一切都好办了, 我们就开始动手吧.

, 实现效果
  每个服务卡片绑定一路公交车, 设置好候车站, 5秒更新公交车前进一个站, 当公交车到达候车站时, 公交车前进进度条停止. 除了显示公交车前进状态, 也显示出前所在位置站牌.

编辑卡片视频-不同规格卡片绑定不同的公交车线路: https://v.youku.com/v_show/id_XNTE4MjAzOTE1Ng==.html?x=&sharefrom=android&sharekey=c7aa0569d2c0d621414c23f1dccb240d0


hw2.png
hw5.png


, 创建工程
  在这当作你已经安装好最新版本DevEco-Studio开发工具, 点击File -> New -> New Project... 弹出Create HarmonyOS Project窗口, 这里我选择空白JS模板创建, 写界面还是JS比较方便些, 对于有一定前端知识的小伙伴来说, 创建JS项目的操作我就不截图, 那就操作两个界面.

, 生成服务卡片
  在左边树形目录entry 右击New -> Service Widget
8.jpg

  给卡片起个靓名字, 同时选择创建哪些规格卡片,既然是原子化服务卡片, 当然是全部选择了.
9.jpg
  点击完成, Java包下自动生成一个widget的包, 该包自动创建卡片相关代码, 里面使用的是工厂模式最通用的单例模式, 同时在JS目录下自动生成一个widgetBus目录, 里面包含服务卡片界面相关代码.

, 主界面开发
  虽然本贴子主要介绍是服务卡片, 但要服务卡片有丰富的内容显示和动画, 还得有后台的大力支持, 比如Data Ability保存卡片需要数据, Service Ability 动态显示公交车前进动画进度条,这里先介绍一下整个项目的结构:
10.jpg

主界面效果:
11.jpg
12.jpg

hml代码以下:
  1. <div class="container">
  2.     <div class="search-container">
  3.         <input id="input" class="input" type="text" value="" maxlength="20"
  4.                headericon="/common/images/search.png" placeholder="搜索线路" onchange="change">
  5.         </input>
  6.     </div>
  7.     <div class="body-container">
  8.         <div class="item" for="{{ value in busList }}" onclick="open({{ value.id }})">
  9.             <div class="item-count"><text>{{ value.busNum }}</text></div>
  10.             <div class="detail-container">
  11.                 <div class="left-container">
  12.                     <div class="left-div"><text>候车站 {{ value.waitingStation }}</text></div>
  13.                     <div class="left-div"><text>开往 {{ value.endStation }}</text></div>
  14.                 </div>
  15.                 <div class="right-container">
  16.                     <div class="right-div"><text class="right-text">{{ value.timeRemaining }} 分钟</text></div>
  17.                     <div class="right-div"><text>{{ value.stationsRemaining }} 站 / {{ value.kmRemaining }} km</text></div>
  18.                 </div>
  19.             </div>
  20.         </div>
  21.     </div>
  22. </div>
复制代码
这里就只贴hml代码, JSCSS可以到gitee上查看源代码

车站牌效果: 手机版可以左右滑动
13.jpg
14.jpg

hml代码以下:
  1. <div class="container">
  2.     <div class="header-container">
  3.         <div class="header-back" onclick="onBack">
  4.             <image src="/common/images/left.png" />
  5.         </div>
  6.         <text>{{ stationDetail[0].parentName }}</text>
  7.     </div>
  8.     <div class="body-container">
  9.         <div class="station-title" ><text>{{ stationDetail[0].startStation }} - {{ stationDetail[0].endStation }}</text></div>
  10.         <list scrollbar="on" style="flex-direction: row;">
  11.             <list-item>
  12.                 <div class="station-detail" style="width: {{totalWidth-(stationWidth-30)}}px;">
  13.                     <progress class="min-progress" type="horizontal" percent= "{{currentPercent}}" secondarypercent="{{secondaryPercent}}"></progress>
  14.                     
  15.                     <div class="detail-text" >
  16.                         <block for="{{ stationDetail }}">
  17.                             <div style="width: {{ $idx == stationDetail.length-1 ? 30 :  stationWidth }}px;">
  18.                                 <text>{{$idx}} {{$item.stationName}}</text>
  19.                             </div>
  20.                         </block>
  21.                     </div>
  22.                 </div>
  23.             </list-item>
  24.         </list>
  25.     </div>
  26. </div>
复制代码

这里就只贴hml代码, JSCSS可以到gitee上查看源代码

, 终于到主角登场了
  原子化服务卡片来了, 不同规格卡片,不同设备显示,都是同一套代码
15.jpg
16.jpg
17.jpg
18.jpg
hml代码以下:
  1. <div class="image_with_info_layout" onclick="routerEvent">
  2.     <div if="{{ mini }}" class="mini_container" >
  3.         <image src="/common/icon.png" class="mini_image"></image>
  4.         <text class="mini_title">{{ miniTitle }}</text>
  5.     </div>
  6.     <div class="normal_container">
  7.         <div class="items_container" style="margin-top : {{ imagePaddingTop }}">
  8.             <div class="item_container" style="display-index : 4;">
  9.                 <text class="item_title">{{ itemTitle }}</text>
  10.                 <div class="item_space"></div>
  11.                 <text class="item_content">{{ itemContent }}</text>
  12.             </div>
  13.             <div class="item_container" style="display-index : 3;">
  14.                 <progress class="min-progress" type="horizontal" percent= "{{currentPercent}}" secondarypercent="{{secondaryPercent}}"></progress>
  15.                 <text if="{{ detailStation }}"  class="item_content">{{ detailContent }}</text>
  16.             </div>
  17.         </div>
  18.         <div class="title_container">
  19.             <div class="title_sub_container" style="flex-direction: row; margin-right: 10px;">
  20.                 <div class="station-detail" style="width: 100%;">
  21.                     <div class="detail-text" >
  22.                         <block for="{{ stationDetail }}">
  23.                             <div style="flex-direction: column;" >
  24.                                 <text class="detail-text-child">{{$idx}}</text>
  25.                                 <text class="detail-text-child">{{$item.stationName}}</text>
  26.                             </div>
  27.                         </block>
  28.                     </div>
  29.                 </div>
  30.             </div>
  31.         </div>
  32.     </div>
  33. </div>
复制代码
这里就只贴hml代码, JSCSS可以到gitee上查看源代码

七, 实体类代码片段:
  1. @Entity(tableName = "form")
  2. public class Form extends OrmObject {

  3.     @PrimaryKey()
  4.     private Long formId;        // 卡片Id
  5.     private String formName;    // 卡片名称
  6.     private Integer dimension;  // 卡片规格
  7.     private Long busId;         // 公交车Id
  8. }
复制代码
  1. <pre>@Entity(tableName = "bus", ignoredColumns = {"checkBtn"}) //“ignoredColumns”表示该字段不需要添加到“bus”表的属性中</pre>public class Bus extends OrmObject {

  2.     @PrimaryKey()
  3.     private Long id; // 主键ID
  4.     private String busNum;  // 线路号 "987路"
  5.     private String waitingStation; // 候车站 "海珠客运站"
  6.     private String endStation; // 开往 "天安科技园"
  7.     private int timeRemaining; // 离候车站剩下分钟 "10分钟"
  8.     private int stationsRemaining;  // 离候车站剩下几站 "5站"
  9.     private float kmRemaining; // 离候车站剩下距离 "1.5km"
复制代码
  1. @Entity(tableName = "station")
  2. public class Station extends OrmObject {

  3.     @PrimaryKey(autoGenerate = true)<pre>  private Integer sId;    // 站牌Id 自动生成</pre>    private String stationName;     // 站牌名 "海珠客运站总站",
  4.     private Long parentId;          // 公交车Id
  5.     private String parentName;      // 公交线路
  6.     private String startStation;    // 起始站
  7.     private String endStation;      // 终点站
  8.     private int displayOrder;       // 站牌序号
  9. }
复制代码
  1. @Database(entities = {Form.class, Bus.class, Station.class}, version = 1)
  2. public abstract class BusComesDatabase extends OrmDatabase {
  3. }
复制代码
要使用@Entity, @PrimaryKey, @Database前, 必须在entry目录下的build.gradle文件添加红色框内容
19.jpg


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


1502.png

在BusCardConfigAbility.onstart中添加setInstanceName("BusCardConfig");
  1. public class BusCardConfigAbility extends AceAbility {
  2.     public static Long formId;

  3.     @Override
  4.     public void onStart(Intent intent) {
  5.         // 绑定前端BusCardConfig页面
  6.         setInstanceName("BusCardConfig");

  7.         // 获取卡片ID并进行保存
  8.         IntentParams params = intent.getParams();
  9.         formId = (long) params.getParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY);
  10.         super.onStart(intent);
  11.     }

  12.     @Override
  13.     public void onStop() {
  14.         super.onStop();
  15.     }
  16. }
复制代码
    2. 在config.json配置文件中增加属性:formConfigAbility
  1. "formConfigAbility": "ability://BusCardConfigAbility"
复制代码


九, 编辑页面开发&编辑更新卡片逻辑开发
       hml代码以下:
  1. <div class="container">
  2.     <div class="search-container">
  3.         <input id="input" class="input" type="text" value="" maxlength="20" onenterkeyclick="change"
  4.                headericon="/common/images/search.png" placeholder="搜索线路" onchange="change">
  5.         </input>
  6.     </div>
  7.     <div class="item" for="{{ value in busList }}" onclick="checked({{ value.id }})">
  8.         <image class="todo-image" src="{{value.checkBtn}}"></image>
  9.         <div class="item-title" ><text class="item-desc">{{ value.busNum }}</text></div>
  10.         <div class="item-detail" >
  11.             <div><text class="item-desc">起始站: {{ value.waitingStation }}</text></div>
  12.             <div><text class="item-desc">终点站: {{ value.endStation }}</text></div>
  13.         </div>
  14.     </div>
  15. </div>
复制代码
     JS代码以下:
  1. import app from '@system.app';
  2. const BUTTON_STATE_IMAGE = ['/common/images/checkbox-blank.png', '/common/images/checkbox-circle.png'];
  3. export default {
  4.     data: {
  5.         "busList": [
  6.             {"id": 1, "busNum": "987路", "waitingStation": "海珠客运站总站", "endStation": "天安科技园总站", "timeRemaining": 5, "stationsRemaining": 1, "kmRemaining": 1.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
  7.             {"id": 2, "busNum": "303路", "waitingStation": "太古仓路总站", "endStation": "海珠客运站总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
  8.             {"id": 3, "busNum": "288路", "waitingStation": "大夫山公园总站", "endStation": "洛溪新城总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
  9.             {"id": 4, "busNum": "129路", "waitingStation": "洛溪新城总站", "endStation": "奥林匹克花园总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
  10.             {"id": 5, "busNum": "230路", "waitingStation": "华工大总站", "endStation": "客村立交总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]},
  11.             {"id": 6, "busNum": "882路", "waitingStation": "客村立交总站", "endStation": "员村总站", "timeRemaining": 20, "stationsRemaining": 3, "kmRemaining": 3.5, "checkBtn": BUTTON_STATE_IMAGE[0]}
  12.         ],
  13.         "keyword": "",
  14.         "busId": -1
  15.     },
  16.     onInit() {
  17.         this.getBusList();
  18.     },
  19.     initAction: function (code, actionData) {
  20.         var action = {};
  21.         action.bundleName = "com.army.study";
  22.         action.abilityName = "com.army.study.service.BusInternalAbility";
  23.         action.messageCode = code;
  24.         action.data = actionData;
  25.         action.abilityType = 1;
  26.         action.syncOption = 0;
  27.         return action;
  28.     },
  29.     getBusList: async function () {
  30.         var actionData = {"keyword": this.keyword};
  31.         try {
  32.             var action = this.initAction(1001, actionData);
  33.             var result = await FeatureAbility.callAbility(action);
  34.             console.info(" @@result = " + JSON.stringify(result));
  35.             this.busList = JSON.parse(result);
  36.         } catch (pluginError) {
  37.             console.error("getBusList : Plugin Error = " + pluginError);
  38.         }
  39.     },
  40.     change(e) {
  41.         this.keyword = e.value;
  42.         this.getBusList();
  43.     },
  44.     bindingCard: async function () {
  45.         var actionData = {"busId": this.busId};
  46.         try {
  47.             var action = this.initAction(1003, actionData);
  48.             var result = await FeatureAbility.callAbility(action);
  49.             console.info(" @@result = " + result);

  50.             if("OK" == result) {
  51.                 // 延时500毫秒关闭当前页面
  52.                 setTimeout(function(){app.terminate();},500);
  53.             }
  54.         } catch (pluginError) {
  55.             console.error("bindingCard : Plugin Error = " + pluginError);
  56.         }
  57.     },
  58.     checked(id) {
  59.         let _this = this;
  60.         _this.busList.forEach(element => {
  61.             element.checkBtn =  element.id == id ? BUTTON_STATE_IMAGE[1] : BUTTON_STATE_IMAGE[0]
  62.         });

  63.         this.busId = id;
  64.         this.bindingCard();
  65.     }
  66. }
复制代码
    CSS代码请移步到gitee查看源码
更新卡片数据片段代码:
  1. // 2. 再根据公交Id获取公交行走路线站点信息
  2.         Bus bus = DatabaseUtils.getBusById(connect, busId);
  3.         List<Station> stationDetail = DatabaseUtils.getStationByParentId(connect, busId);
  4.         System.out.println("stationDetail size: " + stationDetail.size());
  5.         // 3. 封装返回卡片需要信息数据
  6.         ZSONObject zsonObject = new ZSONObject();

  7.         ZSONArray zsonArray = new ZSONArray();
  8.         for (Station obj : stationDetail) {
  9.             ZSONObject station = new ZSONObject();
  10.             station.put("stationName",obj.getStationName());
  11.             station.put("parentId",obj.getParentId());
  12.             station.put("displayOrder",obj.getDisplayOrder());
  13.             zsonArray.add(station);
  14.         }

  15.         zsonObject.put("stationDetail", zsonArray);
  16.         FormBindingData formBindingData = new FormBindingData(zsonObject);
  17.         try {
  18.             // 更新卡片信息
  19.             ((MainAbility)context).updateForm(formId, formBindingData);
  20.         } catch (FormException e) {
  21.             e.printStackTrace();
  22.         }
复制代码
效果图:
1503.jpg





其它Ability代码文件, 都有注释, 有兴趣的小伙伴可以下载源码查看, 项目还不算完整版, 下来会慢慢更新, 源码也会同步到gitee码云

源码在这: https://gitee.com/army16_harmony/bus-comes

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友