HarmonyOS原子化服务卡片-车来了 原创 精华

发布于 2021-7-9 21:49
浏览
16收藏

一, 前言

      自从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

 

HarmonyOS原子化服务卡片-车来了-开源基础软件社区 HarmonyOS原子化服务卡片-车来了-开源基础软件社区
HarmonyOS原子化服务卡片-车来了-开源基础软件社区 HarmonyOS原子化服务卡片-车来了-开源基础软件社区
HarmonyOS原子化服务卡片-车来了-开源基础软件社区
HarmonyOS原子化服务卡片-车来了-开源基础软件社区
HarmonyOS原子化服务卡片-车来了-开源基础软件社区

 

 

三, 创建工程

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

 

四, 生成服务卡片

      在左边树形目录entry 右击New -> Service Widget

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

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

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

        点击完成, 在Java包下自动生成一个widget的包, 该包自动创建卡片相关代码, 里面使用的是工厂模式最通用的单例模式, 同时在JS目录下自动生成一个widgetBus目录, 里面包含服务卡片界面相关代码.

 

五, 主界面开发

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

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

主界面效果:

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

hml代码以下:

<div class="container">
    <div class="search-container">
        <input id="input" class="input" type="text" value="" maxlength="20"
               headericon="/common/images/search.png" placeholder="搜索线路" onchange="change">
        </input>
    </div>
    <div class="body-container">
        <div class="item" for="{{ value in busList }}" onclick="open({{ value.id }})">
            <div class="item-count"><text>{{ value.busNum }}</text></div>
            <div class="detail-container">
                <div class="left-container">
                    <div class="left-div"><text>候车站 {{ value.waitingStation }}</text></div>
                    <div class="left-div"><text>开往 {{ value.endStation }}</text></div>
                </div>
                <div class="right-container">
                    <div class="right-div"><text class="right-text">{{ value.timeRemaining }} 分钟</text></div>
                    <div class="right-div"><text>{{ value.stationsRemaining }} 站 / {{ value.kmRemaining }} km</text></div>
                </div>
            </div>
        </div>
    </div>
</div>

   这里就只贴hml代码, JS和CSS可以到gitee上查看源代码.

   车站牌效果: 手机版可以左右滑动

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

hml代码以下:

<div class="container">
    <div class="header-container">
        <div class="header-back" onclick="onBack">
            <image src="/common/images/left.png" />
        </div>
        <text>{{ stationDetail[0].parentName }}</text>
    </div>
    <div class="body-container">
        <div class="station-title" ><text>{{ stationDetail[0].startStation }} - {{ stationDetail[0].endStation }}</text></div>
        <list scrollbar="on" style="flex-direction: row;">
            <list-item>
                <div class="station-detail" style="width: {{totalWidth-(stationWidth-30)}}px;">
                    <progress class="min-progress" type="horizontal" percent= "{{currentPercent}}" secondarypercent="{{secondaryPercent}}"></progress>
                    
                    <div class="detail-text" >
                        <block for="{{ stationDetail }}">
                            <div style="width: {{ $idx == stationDetail.length-1 ? 30 :  stationWidth }}px;">
                                <text>{{$idx}} {{$item.stationName}}</text>
                            </div>
                        </block>
                    </div>
                </div>
            </list-item>
        </list>
    </div>
</div>

      这里就只贴hml代码, JS和CSS可以到gitee上查看源代码.

六, 终于到主角登场了

      原子化服务卡片来了, 不同规格卡片,不同设备显示,都是同一套代码

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

    hml代码以下:

<div class="image_with_info_layout" onclick="routerEvent">
    <div if="{{ mini }}" class="mini_container" >
        <image src="/common/icon.png" class="mini_image"></image>
        <text class="mini_title">{{ miniTitle }}</text>
    </div>
    <div class="normal_container">
        <div class="items_container" style="margin-top : {{ imagePaddingTop }}">
            <div class="item_container" style="display-index : 4;">
                <text class="item_title">{{ itemTitle }}</text>
                <div class="item_space"></div>
                <text class="item_content">{{ itemContent }}</text>
            </div>
            <div class="item_container" style="display-index : 3;">
                <progress class="min-progress" type="horizontal" percent= "{{currentPercent}}" secondarypercent="{{secondaryPercent}}"></progress>
                <text if="{{ detailStation }}"  class="item_content">{{ detailContent }}</text>
            </div>
        </div>
        <div class="title_container">
            <div class="title_sub_container" style="flex-direction: row; margin-right: 10px;">
                <div class="station-detail" style="width: 100%;">
                    <div class="detail-text" >
                        <block for="{{ stationDetail }}">
                            <div style="flex-direction: column;" >
                                <text class="detail-text-child">{{$idx}}</text>
                                <text class="detail-text-child">{{$item.stationName}}</text>
                            </div>
                        </block>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

这里就只贴hml代码, JS和CSS可以到gitee上查看源代码.

 

七, 实体类代码片段:

@Entity(tableName = "form")
public class Form extends OrmObject {

    @PrimaryKey()
    private Long formId;        // 卡片Id
    private String formName;    // 卡片名称
    private Integer dimension;  // 卡片规格
    private Long busId;         // 公交车Id
}
@Entity(tableName = "bus", ignoredColumns = {"checkBtn"}) //“ignoredColumns”表示该字段不需要添加到“bus”表的属性中
public class Bus extends OrmObject {

    @PrimaryKey()
    private Long id; // 主键ID
    private String busNum;  // 线路号 "987路"
    private String waitingStation; // 候车站 "海珠客运站"
    private String endStation; // 开往 "天安科技园"
    private int timeRemaining; // 离候车站剩下分钟 "10分钟"
    private int stationsRemaining;  // 离候车站剩下几站 "5站"
    private float kmRemaining; // 离候车站剩下距离 "1.5km"

    // 不用保存到数据库
    private String checkBtn; // 编辑卡片, 显示是否选择
}
@Entity(tableName = "station")
public class Station extends OrmObject {

   @PrimaryKey(autoGenerate = true)
    private Integer sId;            // 站牌Id 自动生成
    private String stationName;     // 站牌名 "海珠客运站总站",
    private Long parentId;          // 公交车Id
    private String parentName;      // 公交线路
    private String startStation;    // 起始站
    private String endStation;      // 终点站
    private int displayOrder;       // 站牌序号
}
@Database(entities = {Form.class, Bus.class, Station.class}, version = 1)
public abstract class BusComesDatabase extends OrmDatabase {
}

要使用@Entity, @PrimaryKey, @Database前, 必须在entry目录下的build.gradle文件添加红色框内容

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

八, 增加长按卡片编辑页面

      1. 创建卡片编辑Ability(BusCardConfigAbility)

            右击entry>New>Ability>Page Ability(JS)

HarmonyOS原子化服务卡片-车来了-开源基础软件社区HarmonyOS原子化服务卡片-车来了-开源基础软件社区在BusCardConfigAbility.onstart中添加setInstanceName("BusCardConfig");

public class BusCardConfigAbility extends AceAbility {
    public static Long formId;

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

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

    @Override
    public void onStop() {
        super.onStop();
    }
}

     2. 在config.json配置文件中增加属性:formConfigAbility

"formConfigAbility": "ability://BusCardConfigAbility"

九, 编辑页面开发&编辑更新卡片逻辑开发

       hml代码以下:

<div class="container">
    <div class="search-container">
        <input id="input" class="input" type="text" value="" maxlength="20" onenterkeyclick="change"
               headericon="/common/images/search.png" placeholder="搜索线路" onchange="change">
        </input>
    </div>
    <div class="item" for="{{ value in busList }}" onclick="checked({{ value.id }})">
        <image class="todo-image" src="{{value.checkBtn}}"></image>
        <div class="item-title" ><text class="item-desc">{{ value.busNum }}</text></div>
        <div class="item-detail" >
            <div><text class="item-desc">起始站: {{ value.waitingStation }}</text></div>
            <div><text class="item-desc">终点站: {{ value.endStation }}</text></div>
        </div>
    </div>
</div>

      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) {
                // 延时500毫秒关闭当前页面
                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();
    }
}

     CSS代码请移步到gitee查看源码

 

更新卡片数据片段代码:

// 2. 再根据公交Id获取公交行走路线站点信息
        Bus bus = DatabaseUtils.getBusById(connect, busId);
        List<Station> stationDetail = DatabaseUtils.getStationByParentId(connect, busId);
        System.out.println("stationDetail size: " + stationDetail.size());
        // 3. 封装返回卡片需要信息数据
        ZSONObject zsonObject = new ZSONObject();

        ZSONArray zsonArray = new ZSONArray();
        for (Station obj : stationDetail) {
            ZSONObject station = new ZSONObject();
            station.put("stationName",obj.getStationName());
            station.put("parentId",obj.getParentId());
            station.put("displayOrder",obj.getDisplayOrder());
            zsonArray.add(station);
        }

        zsonObject.put("stationDetail", zsonArray);
        FormBindingData formBindingData = new FormBindingData(zsonObject);
        try {
            // 更新卡片信息
            ((MainAbility)context).updateForm(formId, formBindingData);
        } catch (FormException e) {
            e.printStackTrace();
        }

效果图:

HarmonyOS原子化服务卡片-车来了-开源基础软件社区

 

 

 

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

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

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-7-15 18:22:19修改
16
收藏 16
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐