鸿蒙卡片-中国空间站太阳能板小卡片 原创 精华

发布于 2021-6-21 22:57
浏览
1收藏

演示视频:
假如中国空间站用上了鸿蒙系统

鸿蒙卡片-物联网DTU污水液位计卡片

1. 前言&项目背景

鸿蒙在手机beta3中新增了桌面卡片,我也是在第一时间体验了一下新浪新闻鸿蒙版的新闻小卡片,我觉得非常有意思,并且我觉得可以用在物联网项目的污水液位计的液位展示中,可以实现碰一碰获取液位计数据展示在小卡片中。

HarmonyOS推出的服务卡片,是FA(Feature Ability)的界面展现形式,将FA的重要信息或者操作前置到卡片上,以达到服务直达的目的。

系统自带卡片实际界面如下图所示:
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

2. 服务卡片的UI设计

2.1尺寸选择

鸿蒙的服务卡片尺寸分别为:微(1×2)、小( 2×2 )、中( 2×4 )、大(4×4)4种尺寸。
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
服务卡片示例-From:作者:zengsi,华为软件开发工程师
污水液位计卡片展示数据比较少,所以我选了2×2的小卡片,和2×4的小卡片。2*2的小卡片主要是展示的是单个液位计的数据,而2×4的小卡片展示的是多个液位计的数据的总体展示

2.2 内容构成

服务卡片由多种设计元素组合而成,以下7种常见信息元素可以作为内容选择:

图标、数据、文本、按钮、图片、宫格、列表

污水液位计卡片我觉得主要展示的是当前的液位和液位计的历史曲线,所以我这个污水液位计卡片由数据、文本、曲线图、列表和按钮组成。

2.3 污水液位计卡片原型设计

采用水平垂直居中的布局,因为重要的数据是当前液位,所以当前液位采用18px的黑色字体,点击查看详情用是#53A7F3颜色的15px字,曲线使用#53A7F3颜色更显科技感
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

3. 基本概念

3.1 卡片使用方

显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。

3.2 卡片管理服务

用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。

3.3 卡片提供方

提供卡片显示内容的HarmonyOS应用或原子化服务,控制卡片的显示内容、控件布局以及控件点击事件。

来自:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ability-service-widget-overview-0000001062607955

4. 运作机制

鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
卡片管理服务包含以下模块:

1.周期性刷新:在卡片添加后,根据卡片的刷新策略启动定时任务周期性触发卡片的刷新。
2.卡片缓存管理:在卡片添加到卡片管理服务后,对卡片的视图信息进行缓存,以便下次获取卡片时可以直接返回缓存数据,降低时延。
3.卡片生命周期管理:对于卡片切换到后台或者被遮挡时,暂停卡片的刷新;以及卡片的升级/卸载场景下对卡片数据的更新和清理。
4.卡片使用方对象管理:对卡片使用方的RPC对象进行管理,用于使用方请求进行校验以及对卡片更新后的回调处理。
5.通信适配层:负责与卡片使用方和提供方进行RPC通信。

卡片提供方包含以下模块:

1.卡片服务:由卡片提供方开发者实现,开发者实现onCreateForm、onUpdateForm和onDeleteForm处理创建卡片、更新卡片以及删除卡片等请求,提供相应的卡片服务。
卡片提供方实例管理模块:由卡片提供方开发者实现,负责对卡片管理服务分配的卡片实例进行持久化管理。
2.通信适配层:由HarmonyOS SDK提供,负责与卡片管理服务通信,用于将卡片的更新数据主动推送到卡片管理服务。
来自:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ability-service-widget-overview-0000001062607955

5. 服务卡片开发环境搭建和基本页面开发

5.1 开发环境

1.一台升级了鸿蒙2.0的手机/登录华为开发者账号使用远程模拟器

2.下载安装DevEco Studio 2.1 Release

DevEco下载安装教程:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/installation_process-0000001071425528

因为本文的重点是卡片,deveco安装教程和真机调试请看下面我的教程

https://blog.csdn.net/qq_33259323/article/details/112405157

5.2 新建HarmonyOS手机项目

API选择5,show in service senter打勾
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

5.3 卡片基础配置

然后打开配置文件src/main/config.json,配置你所需要的卡片样式,详细配置请看

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/basic-config-file-elements-0000000000034463#ZH-CN_TOPIC_0000001064016070__table8276925145611
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
我这边选择的是2*2的小卡片,其他的都是默认所以只需要改一下名字和描述,注意文件夹名字要和name对应,如果不对应就是白卡片
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

5.4 卡片基础界面编写代码

编写hml,通过{{}}绑定index.json里面的数据

<div class="container">
    <div class="title">
        <text class="text_title">1#液位计: </text>
        <text class="text_title">{{temperature}}</text>
        <text class="text_title"> m</text>
    </div>
    <stack class="chart_region">
        <chart class="chart_data" type="line" options="{{lineOps}}" datasets="{{lineData}}"></chart>
    </stack>
    <text class="text_nav">点击查看详情</text>
</div>

编写CSS

.container {
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
 
.title{
    width: 100%;
    height: 30px;
    justify-content: center;
}
 
.text_title {
    font-size: 15px;
}
 
.chart_region{
    height: 90px;
}
 
.chart_data{
 
}
 
.text_nav {
    font-size: 15px;
    color: #53A7F3;
}

编写JSON

{
  "data": {
    "level": "12",
    "lineData": [
      {
        "strokeColor": "#7fccde",
        "fillColor": "#7fccde",
        "data": [0,10,20,12,13,10,40,10,5,9,14,18,20,30,10,20,10,17],
        "gradient": true
      }
    ],
    "lineOps": {
      "xAxis": {
        "min": 0,
        "max": 15,
        "display": false
      },
      "yAxis": {
        "min": 0,
        "max": 24,
        "display": false
      },
      "series": {
        "lineStyle": {
          "width": "1px",
          "smooth": true
        },
        "headPoint": {
          "shape": "circle",
          "size": 10,
          "strokeWidth": 3,
          "fillColor": "#ffffff",
          "strokeColor": "#7fccde",
          "display": true
        }
      }
    }
  }
}

可以先使用预览器看一下界面,或者直接运行

双击打开index.hml,然后点击右侧的预览器
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

6. 服务卡片基本开发教程

6.1 增加点击跳转查看详情页面

1.在hml增加点击事件
<text class="text_nav" onclick="routerEvent">点击查看详情</text>
2.创建需要跳转的Ability(CardFormAbility)

鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

3.编写index.json文件

其中 routerEvent 就是在hml中的onclick属性值,action为router,abilityName为需要跳转到的ability名字
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

4.编写跳转测试页面
package com.example.phone.ability;
 
import ohos.ace.ability.AceAbility;
import ohos.aafwk.content.Intent;
 
public class CardFormAbility extends AceAbility {
    @Override
    public void onStart(Intent intent) {
        setInstanceName("CardForm");
        super.onStart(intent);
    }
 
    @Override
    public void onStop() {
        super.onStop();
    }
}

鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

6.2 增加简单的长按编辑页面

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

点击File>New>Ability>Page Ability(JS)
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
在LevelCardConfigAbility.onstart中添加setInstanceName(“LevelCardConfig”);

package com.example.phone.ability;
 
import ohos.ace.ability.AceAbility;
import ohos.aafwk.content.Intent;
 
public class LevelCardConfigAbility extends AceAbility {
    @Override
    public void onStart(Intent intent) {
        setInstanceName("LevelCardConfig");
        super.onStart(intent);
    }
 
    @Override
    public void onStop() {
        super.onStop();
    }
}

在hml中添加示例代码
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

2.在配置文件中增加属性:formConfigAbility
"formConfigAbility": "ability://com.example.phone.ability.LevelCardConfigAbility"

鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
污水液位计卡片编辑页面详细开发请看:6.9

6.3 数据手动刷新

1. 在卡片编辑小卡片添加手动刷新事件

index.hml
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
index.json
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

2. 创建CardFormAbility(如果之前已经创建过了就不用创建了)

在src/main/config.json中,如果你的小卡片是写在MainAbility里面的,就不需要创建这个CardFormAbility,我是为了方便分开来,把卡片配置写在CardFormAbility中
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
因为是演示代码,所以请求后台服务器获得数据的代码放在onTriggerFormEvent中

package com.example.phone.ability;
 
import ohos.aafwk.ability.FormBindingData;
import ohos.aafwk.ability.FormException;
import ohos.ace.ability.AceAbility;
import ohos.aafwk.content.Intent;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.zson.ZSONObject;
 
public class CardFormAbility extends AceAbility {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "CardFormAbility");
 
    @Override
    public void onStart(Intent intent) {
        setInstanceName("CardForm");
        super.onStart(intent);
    }
 
    @Override
    public void onStop() {
        super.onStop();
    }
 
    @Override
    protected void onTriggerFormEvent(long formId, String message) {
        // 解析收到的数据
        ZSONObject messageJSON = ZSONObject.stringToZSON(message);
 
        if(messageJSON.get("message").equals("updata")){       // 更新数据
            ZSONObject zsonObject = new ZSONObject();
            
            // 请求后台服务器获得数据
            zsonObject.put("level", "100");
            
            FormBindingData formBindingData = new FormBindingData(zsonObject);
            try {
                // 更新数据
                if (!updateForm(formId, formBindingData)) {
 
                }
            } catch (FormException e) {
                e.printStackTrace();
            }
        }
 
        super.onTriggerFormEvent(formId, message);
    }
}

这样点击index.hml中的标题,就可以更新数据了

7. 服务卡片进阶开发教程

7.1 数据定时刷新

7.1.1 使用鸿蒙自带的定时刷新

1. 数据定时刷新需要在src/main/config.json配置文件中配置,是否开启定时刷新和定时刷新的时间,在6.3中有介绍
"updateEnabled": true,
"updateDuration": 1
2. 编写CardFormAbility,重写onUpdateForm方法
@Override
protected void onUpdateForm(long formId) {
        super.onUpdateForm(formId);
        ZSONObject zsonObject = new ZSONObject();
        zsonObject.put("level", "1.123");
        FormBindingData formBindingData = new FormBindingData(zsonObject);
        // 调用updateForm接口去更新对应的卡片,仅更新入参中携带的数据信息,其他信息保持不变
        try {
            if (!updateForm(formId, formBindingData)) {
                // err process
            }
        } catch (FormException e) {
            e.printStackTrace();
        }
}

7.1.2 自定义刷新策略

请看7.3

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

1.页面开发

index.hml

<div class="container">
    <text class="title">
        选择液位计
    </text>
    <list class="todo-wraper">
        <list-item for="{{todolist}}" class="todo-item" @click="choose({{$item.id}}})">
            <text class="todo-title">{{$item.title}}</text>
        </list-item>
    </list>
</div>

index.css

.container {
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
 
.title {
    font-size: 40px;
    color: #000000;
    opacity: 0.9;
}
 
.todo-wraper {
    width: 454px;
    height: 300px;
    margin-top: 20px;
}
.todo-item {
    width: 454px;
    height: 80px;
    flex-direction: column;
}
.todo-title {
    width: 454px;
    height: 40px;
    text-align: center;
}

index.js

import prompt from '@system.prompt';
 
const ABILITY_TYPE_EXTERNAL = 0;
const ACTION_SYNC = 0;
const CHOOSE_LEVEL = 1001;
 
// 给CardServiceAbility发送选择的ID
export const CardFormAbility = {
    choose: async function(id){
        var action = {};
        action.bundleName = 'com.example.phone';
        action.abilityName = 'com.example.phone.ability.CardServiceAbility';
        action.messageCode = CHOOSE_LEVEL;
        action.data = id;
        action.abilityType = ABILITY_TYPE_EXTERNAL;
        action.syncOption = ACTION_SYNC;
 
        var result = await FeatureAbility.callAbility(action);
        var ret = JSON.parse(result);
        if (ret.code == 0) {
 
        } else {
 
        }
    }
}
 
export default {
    data: {
        title: "",
        todolist: [{
            title: '1#液位计',
            id: 1
        }, {
            title: '2#液位计',
            id: 2
        },{
            title: '3#液位计',
            id: 3
        }],
    },
    onInit() {
        this.title = this.$t('strings.world');
    },
    choose(id) {
        
        CardFormAbility.choose(id);
    }
}
2.编写LevelCardConfigAbility来保存卡片ID
package com.example.phone.ability;
 
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.IntentParams;
import ohos.ace.ability.AceAbility;
import ohos.aafwk.content.Intent;
 
public class LevelCardConfigAbility extends AceAbility {
 
    public static Long cardId;
 
    @Override
    public void onStart(Intent intent) {
        setInstanceName("LevelCard");
        // 获取卡片ID并进行保存
        IntentParams params = intent.getParams();
        cardId = (long) params.getParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY);
 
        super.onStart(intent);
    }
 
    @Override
    public void onStop() {
        super.onStop();
    }
}
3.创建CardServiceAbility来获取配置页面的配置信息并且更新卡片
package com.example.phone.ability;
 
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.FormBindingData;
import ohos.aafwk.ability.FormException;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.app.Context;
import ohos.rpc.*;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.zson.ZSONObject;
 
public class CardServiceAbility extends Ability {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
 
    private CardServiceAbility.CardServiceRemote cardServiceRemote;
    private static final int CHOOSE_LEVEL = 1001;
 
    @Override
    public void onStart(Intent intent) {
        HiLog.error(LABEL_LOG, "CardServiceAbility::onStart");
        cardServiceRemote = new CardServiceRemote();
        super.onStart(intent);
    }
 
    @Override
    protected IRemoteObject onConnect(Intent intent) {
        super.onConnect(intent);
        return cardServiceRemote.asObject();
    }
 
    @Override
    public void onDisconnect(Intent intent) {
    }
 
 
    class CardServiceRemote extends RemoteObject implements IRemoteBroker {
 
        public CardServiceRemote() {
            super("CardServiceRemote");
        }
 
        @Override
        public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) throws RemoteException {
            switch (code) {
                case CHOOSE_LEVEL:{
 
                    String zsonStr = data.readString();
 
                    ZSONObject zsonObject = new ZSONObject();
                    zsonObject.put("name", zsonStr+"#液位计:");
                    FormBindingData formBindingData = new FormBindingData(zsonObject);
                    try {
                        if (!updateForm(LevelCardConfigAbility.cardId, formBindingData)) {
                            // err process
                        }
                    } catch (FormException e) {
                        e.printStackTrace();
                    }
 
                    break;
                }
                default: {
                    reply.writeString("service not defined");
                    return false;
                }
            }
            return true;
        }
 
        @Override
        public IRemoteObject asObject() {
            return this;
        }
    }
}

效果:
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区
编辑页面详情:
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

7.3 自定义刷新策略

7.3.1 关系型数据库加入包

1.1 在对应的entry的build.gradle中添加包

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
    testCompile 'junit:junit:4.12'
 
    compile files(ORM_ANNOTATIONS_JAVA, ORM_ANNOTATIONS_PROCESSOR_JAVA, JAVAPOET_JAVA)
    annotationProcessor files(ORM_ANNOTATIONS_JAVA, ORM_ANNOTATIONS_PROCESSOR_JAVA, JAVAPOET_JAVA)
}

1.2 在gradle.properties中添加gradle全局变量

JAVAPOET_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/javapoet_java.jar
ORM_ANNOTATIONS_PROCESSOR_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/orm_annotations_processor_java.jar
ORM_ANNOTATIONS_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/orm_annotations_java.jar

1.3 重新构建

7.3.2 创建数据库类和表类

1.数据库类
例如,定义了一个数据库类LevelStore.java,数据库包含了“Level”表,版本号为“1”。数据库类的getVersion方法和getHelper方法不需要实现,直接将数据库类设为虚类即可。

package com.example.phone.store;
 
import com.example.phone.store.from.Level;
import ohos.data.orm.OrmDatabase;
import ohos.data.orm.annotation.Database;
 
@Database(entities = {Level.class}, version = 1)
public abstract class LevelStore extends OrmDatabase {
}

2.创建表类

package com.example.phone.store.from;
 
import ohos.data.orm.OrmObject;
import ohos.data.orm.annotation.Entity;
import ohos.data.orm.annotation.PrimaryKey;
 
@Entity(tableName = "level")
public class Level extends OrmObject {
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public Level() {
 
    }
 
    public Level(Long id, String name) {
        this.id = id;
        this.name = name;
    }
 
    @Override
    public String toString() {
        return "LevelCard{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @PrimaryKey(autoGenerate = true)
    private Long id;
 
    private String name;
 
}

7.3.3 卡片数据将存在数据库并定时刷新

private static OrmContext ormContext = null;
private DatabaseHelper helper = new DatabaseHelper(this);
@Override
protected ProviderFormInfo onCreateForm(Intent intent) {
    
        IntentParams params = intent.getParams();
        if (params == null) {
            return null;
        }
        // 卡片ID
        Long formId = (long) params.getParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY);
        // 卡片名称
        String formName = (String) params.getParam(AbilitySlice.PARAM_FORM_NAME_KEY);
        // 卡片规格信息
        int specificationId = (int) params.getParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY);
        System.out.println( "创建卡片: " + formId + " " + formName + " " + specificationId);
 
        if(ormContext == null){
            createDataBase(getContext());
        }
 
        // 存储数据
        Level newLevel = new Level(formId, formName);
        boolean isSuccessed = ormContext.insert(newLevel);
        isSuccessed = ormContext.flush();
    
 
}
 
public void createDataBase(Context context){
        // 创建数据库
        ormContext = helper.getOrmContext("LevelStore", "LevelStore.db",     LevelStore.class);
        // 启动定时刷新程序
        startTimer();
}
 
private void startTimer(){
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // 查询数据库获取数据
                OrmPredicates query = ormContext.where(Level.class);
                List<Level> levelCard = ormContext.query(query);
                ZSONObject zsonObject = new ZSONObject();
 
                try {
                    for (Level l:levelCard){
                        Long formId = l.getId();
 
                        // 设置数据
                        double randomLevel = Math.random()*10;
                        DecimalFormat randomLevelDf = new DecimalFormat( "0.00");
                        zsonObject.put("level", randomLevelDf.format(randomLevel));
 
                        FormBindingData formBindingData = new FormBindingData(zsonObject);
                        if (!updateForm(formId, formBindingData)) {
                            deleteLevelCard(formId);
                        }
                    }
                } catch (FormException e) {
                    e.printStackTrace();
                }
            }
        },5,700L);
    }

效果展示,演示视频:
鸿蒙卡片-中国空间站太阳能板小卡片-开源基础软件社区

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