6 月 17 日,神舟十二号发射圆满成功。据了解,中国航天使用的是我国自主研发的麒麟操作系统,“天问一号”成功着陆火星使用的就是该系统。不少网友误认为是华为操作系统,而华为用是鸿蒙系统和麒麟芯片,与航天用的麒麟系统根本没什么关系。
那么假如中国空间站用上了鸿蒙系统会怎么样呢?下面手把手教大家开发中国空间站专属太阳能板鸿蒙小卡片!
01
项目背景
鸿蒙在手机 Beta3 中新增了桌面卡片,我也是在第一时间体验了一下新浪新闻鸿蒙版的新闻小卡片,觉得非常有意思。
并且我觉得可以用在物联网项目的污水液位计的液位展示中,可以实现碰一碰获取液位计数据展示在小卡片中。
HarmonyOS 推出的服务卡片,是 FA(Feature Ability)的界面展现形式,将 FA 的重要信息或者操作前置到卡片上,以达到服务直达的目的。
02
服务卡片的 UI 设计
①尺寸选择
鸿蒙的服务卡片尺寸分别为:微(1×2)、小( 2×2 )、中( 2×4 )、大(4×4)4 种尺寸。
污水液位计卡片展示数据比较少,所以我选了 2×2 的小卡片,和 2×4 的小卡片。
2*2 的小卡片主要是展示的是单个液位计的数据,而 2×4 的小卡片展示的是多个液位计的数据的总体展示。
②内容构成
服务卡片由多种设计元素组合而成,以下 7 种常见信息元素可以作为内容选择:图标、数据、文本、按钮、图片、宫格、列表。
污水液位计卡片我觉得主要展示的是当前的液位和液位计的历史曲线,所以我这个污水液位计卡片由数据、文本、曲线图、列表和按钮组成。
③污水液位计卡片原型设计
采用水平垂直居中的布局,因为重要的数据是当前液位,所以当前液位采用 18px 的黑色字体,点击查看详情用是 #53A7F3 颜色的 15px 字,曲线使用 #53A7F3 颜色更显科技感。
03
基本概念
①卡片使用方
显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
②卡片管理服务
用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。
③卡片提供方
提供卡片显示内容的 HarmonyOS 应用或原子化服务,控制卡片的显示内容、控件布局以及控件点击事件。
04
运作机制
卡片管理服务包含以下模块:
周期性刷新:在卡片添加后,根据卡片的刷新策略启动定时任务周期性触发卡片的刷新。
卡片缓存管理:在卡片添加到卡片管理服务后,对卡片的视图信息进行缓存,以便下次获取卡片时可以直接返回缓存数据,降低时延。
卡片生命周期管理:对于卡片切换到后台或者被遮挡时,暂停卡片的刷新;以及卡片的升级/卸载场景下对卡片数据的更新和清理。
卡片使用方对象管理:对卡片使用方的 RPC 对象进行管理,用于使用方请求进行校验以及对卡片更新后的回调处理。
通信适配层:负责与卡片使用方和提供方进行 RPC 通信。
卡片提供方包含以下模块:
①卡片服务:由卡片提供方开发者实现,开发者实现 onCreateForm、onUpdateForm 和 onDeleteForm 处理创建卡片、更新卡片以及删除卡片等请求,提供相应的卡片服务。
卡片提供方实例管理模块:由卡片提供方开发者实现,负责对卡片管理服务分配的卡片实例进行持久化管理。
②通信适配层:由 HarmonyOS SDK 提供,负责与卡片管理服务通信,用于将卡片的更新数据主动推送到卡片管理服务。
05
服务卡片开发环境搭建和开发
①开发环境
一台升级了鸿蒙 2.0 的手机/登录华为开发者账号使用远程模拟器。
下载安装 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
②新建 HarmonyOS 手机项目
API 选择 5,show in service senter 打勾
③卡片基础配置
然后打开配置文件 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 对应,如果不对应就是白卡片。
④卡片基础界面编写代码
编写 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,然后点击右侧的预览器
06
服务卡片基本开发教程
①增加点击跳转查看详情页面
在 hml 增加点击事件:
《text class=“text_nav” onclick=“routerEvent”》点击查看详情《/text》
创建需要跳转的 Ability(CardFormAbility):
编写 index.json 文件,其中 routerEvent 就是在 hml 中的 onclick 属性值,action 为 router,abilityName 为需要跳转到的 ability 名字。
编写跳转测试页面:
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();
}
}
②增加简单的长按编辑页面
创建卡片编辑 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 中添加示例代码:
在配置文件中增加属性:formConfigAbility。
“formConfigAbility”: “ability://com.example.phone.ability.LevelCardConfigAbility”
污水液位计卡片编辑页面详细开发请看下面。
③数据手动刷新
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 中的标题,就可以更新数据了。
07
服务卡片进阶开发教程
①数据定时刷新
使用鸿蒙自带的定时刷新:数据定时刷新需要在 src/main/config.json 配置文件中配置,是否开启定时刷新和定时刷新的时间:
“updateEnabled”: true,
“updateDuration”: 1
编写 CardFormAbility,重写 onUpdateForm 方法:
@Overrideprotected 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();
}
}
自定义刷新策略,请往下看。
②编辑页面开发&编辑更新卡片逻辑开发
页面开发
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发送选择的IDexport 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);
}
}
编写 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();
}
}
创建 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;
}
}
}
③自定义刷新策略
关系型数据库加入包:在对应的 entry 的 build.gradle 中添加包。
dependencies {
implementation fileTree(dir: ‘libs’, include: [‘*.jar’, ‘*.har’])
testCompile ‘junit4.12’
compile files(ORM_ANNOTATIONS_JAVA, ORM_ANNOTATIONS_PROCESSOR_JAVA, JAVAPOET_JAVA)
annotationProcessor files(ORM_ANNOTATIONS_JAVA, ORM_ANNOTATIONS_PROCESSOR_JAVA, JAVAPOET_JAVA)
}
在 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
重新构建:创建数据库类和表类。
数据库类:例如,定义了一个数据库类 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 {
}
创建表类:
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;
}
卡片数据将存在数据库并定时刷新:
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);
}
责任编辑:haq
-
华为
+关注
关注
215文章
34223浏览量
250831 -
鸿蒙系统
+关注
关注
183文章
2633浏览量
66093 -
HarmonyOS
+关注
关注
79文章
1964浏览量
29942
原文标题:假如中国空间站用上鸿蒙系统...
文章出处:【微信号:gh_834c4b3d87fe,微信公众号:OpenHarmony技术社区】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论