6 月 17 日,神舟十二號(hào)發(fā)射圓滿成功。據(jù)了解,中國(guó)航天使用的是我國(guó)自主研發(fā)的麒麟操作系統(tǒng),“天問一號(hào)”成功著陸火星使用的就是該系統(tǒng)。不少網(wǎng)友誤認(rèn)為是華為操作系統(tǒng),而華為用是鴻蒙系統(tǒng)和麒麟芯片,與航天用的麒麟系統(tǒng)根本沒什么關(guān)系。
那么假如中國(guó)空間站用上了鴻蒙系統(tǒng)會(huì)怎么樣呢?下面手把手教大家開發(fā)中國(guó)空間站專屬太陽能板鴻蒙小卡片!
01
項(xiàng)目背景
鴻蒙在手機(jī) Beta3 中新增了桌面卡片,我也是在第一時(shí)間體驗(yàn)了一下新浪新聞鴻蒙版的新聞小卡片,覺得非常有意思。
并且我覺得可以用在物聯(lián)網(wǎng)項(xiàng)目的污水液位計(jì)的液位展示中,可以實(shí)現(xiàn)碰一碰獲取液位計(jì)數(shù)據(jù)展示在小卡片中。
HarmonyOS 推出的服務(wù)卡片,是 FA(Feature Ability)的界面展現(xiàn)形式,將 FA 的重要信息或者操作前置到卡片上,以達(dá)到服務(wù)直達(dá)的目的。
02
服務(wù)卡片的 UI 設(shè)計(jì)
①尺寸選擇
鴻蒙的服務(wù)卡片尺寸分別為:微(1×2)、?。?2×2 )、中( 2×4 )、大(4×4)4 種尺寸。
污水液位計(jì)卡片展示數(shù)據(jù)比較少,所以我選了 2×2 的小卡片,和 2×4 的小卡片。
2*2 的小卡片主要是展示的是單個(gè)液位計(jì)的數(shù)據(jù),而 2×4 的小卡片展示的是多個(gè)液位計(jì)的數(shù)據(jù)的總體展示。
②內(nèi)容構(gòu)成
服務(wù)卡片由多種設(shè)計(jì)元素組合而成,以下 7 種常見信息元素可以作為內(nèi)容選擇:圖標(biāo)、數(shù)據(jù)、文本、按鈕、圖片、宮格、列表。
污水液位計(jì)卡片我覺得主要展示的是當(dāng)前的液位和液位計(jì)的歷史曲線,所以我這個(gè)污水液位計(jì)卡片由數(shù)據(jù)、文本、曲線圖、列表和按鈕組成。
③污水液位計(jì)卡片原型設(shè)計(jì)
采用水平垂直居中的布局,因?yàn)橹匾臄?shù)據(jù)是當(dāng)前液位,所以當(dāng)前液位采用 18px 的黑色字體,點(diǎn)擊查看詳情用是 #53A7F3 顏色的 15px 字,曲線使用 #53A7F3 顏色更顯科技感。
03
基本概念
①卡片使用方
顯示卡片內(nèi)容的宿主應(yīng)用,控制卡片在宿主中展示的位置。
②卡片管理服務(wù)
用于管理系統(tǒng)中所添加卡片的常駐代理服務(wù),包括卡片對(duì)象的管理與使用,以及卡片周期性刷新等。
③卡片提供方
提供卡片顯示內(nèi)容的 HarmonyOS 應(yīng)用或原子化服務(wù),控制卡片的顯示內(nèi)容、控件布局以及控件點(diǎn)擊事件。
04
運(yùn)作機(jī)制
卡片管理服務(wù)包含以下模塊:
周期性刷新:在卡片添加后,根據(jù)卡片的刷新策略啟動(dòng)定時(shí)任務(wù)周期性觸發(fā)卡片的刷新。
卡片緩存管理:在卡片添加到卡片管理服務(wù)后,對(duì)卡片的視圖信息進(jìn)行緩存,以便下次獲取卡片時(shí)可以直接返回緩存數(shù)據(jù),降低時(shí)延。
卡片生命周期管理:對(duì)于卡片切換到后臺(tái)或者被遮擋時(shí),暫??ㄆ乃⑿?;以及卡片的升級(jí)/卸載場(chǎng)景下對(duì)卡片數(shù)據(jù)的更新和清理。
卡片使用方對(duì)象管理:對(duì)卡片使用方的 RPC 對(duì)象進(jìn)行管理,用于使用方請(qǐng)求進(jìn)行校驗(yàn)以及對(duì)卡片更新后的回調(diào)處理。
通信適配層:負(fù)責(zé)與卡片使用方和提供方進(jìn)行 RPC 通信。
卡片提供方包含以下模塊:
①卡片服務(wù):由卡片提供方開發(fā)者實(shí)現(xiàn),開發(fā)者實(shí)現(xiàn) onCreateForm、onUpdateForm 和 onDeleteForm 處理創(chuàng)建卡片、更新卡片以及刪除卡片等請(qǐng)求,提供相應(yīng)的卡片服務(wù)。
卡片提供方實(shí)例管理模塊:由卡片提供方開發(fā)者實(shí)現(xiàn),負(fù)責(zé)對(duì)卡片管理服務(wù)分配的卡片實(shí)例進(jìn)行持久化管理。
②通信適配層:由 HarmonyOS SDK 提供,負(fù)責(zé)與卡片管理服務(wù)通信,用于將卡片的更新數(shù)據(jù)主動(dòng)推送到卡片管理服務(wù)。
05
服務(wù)卡片開發(fā)環(huán)境搭建和開發(fā)
①開發(fā)環(huán)境
一臺(tái)升級(jí)了鴻蒙 2.0 的手機(jī)/登錄華為開發(fā)者賬號(hào)使用遠(yuǎn)程模擬器。
下載安裝 DevEco Studio 2.1 Release,DevEco下載安裝教程:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/installation_process-0000001071425528
因?yàn)楸疚牡闹攸c(diǎn)是卡片,deveco 安裝教程和真機(jī)調(diào)試請(qǐng)看下面我的教程:
https://blog.csdn.net/qq_33259323/article/details/112405157
②新建 HarmonyOS 手機(jī)項(xiàng)目
API 選擇 5,show in service senter 打勾
③卡片基礎(chǔ)配置
然后打開配置文件 src/main/config.json,配置你所需要的卡片樣式,詳細(xì)配置請(qǐng)看:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/basic-config-file-elements-0000000000034463#ZH-CN_TOPIC_0000001064016070__table8276925145611
我這邊選擇的是 2*2 的小卡片,其他的都是默認(rèn)所以只需要改一下名字和描述,注意文件夾名字要和 name 對(duì)應(yīng),如果不對(duì)應(yīng)就是白卡片。
④卡片基礎(chǔ)界面編寫代碼
編寫 hml,通過 {{}} 綁定 index.json 里面的數(shù)據(jù):
《div class=“container”》
《div class=“title”》
《text class=“text_title”》1#液位計(jì): 《/text》
《text class=“text_title”》{{temperature}}《/text》
《text class=“text_title”》 m《/text》
《/div》
《stack class=“chart_region”》
《chart class=“chart_data” type=“l(fā)ine” options=“{{lineOps}}” datasets=“{{lineData}}”》《/chart》
《/stack》
《text class=“text_nav”》點(diǎn)擊查看詳情《/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”: {
“l(fā)evel”: “12”,
“l(fā)ineData”: [
{
“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
}
],
“l(fā)ineOps”: {
“xAxis”: {
“min”: 0,
“max”: 15,
“display”: false
},
“yAxis”: {
“min”: 0,
“max”: 24,
“display”: false
},
“series”: {
“l(fā)ineStyle”: {
“width”: “1px”,
“smooth”: true
},
“headPoint”: {
“shape”: “circle”,
“size”: 10,
“strokeWidth”: 3,
“fillColor”: “#ffffff”,
“strokeColor”: “#7fccde”,
“display”: true
}
}
}
}
}
可以先使用預(yù)覽器看一下界面,或者直接運(yùn)行。雙擊打開 index.hml,然后點(diǎn)擊右側(cè)的預(yù)覽器
06
服務(wù)卡片基本開發(fā)教程
①增加點(diǎn)擊跳轉(zhuǎn)查看詳情頁面
在 hml 增加點(diǎn)擊事件:
《text class=“text_nav” onclick=“routerEvent”》點(diǎn)擊查看詳情《/text》
創(chuàng)建需要跳轉(zhuǎn)的 Ability(CardFormAbility):
編寫 index.json 文件,其中 routerEvent 就是在 hml 中的 onclick 屬性值,action 為 router,abilityName 為需要跳轉(zhuǎn)到的 ability 名字。
編寫跳轉(zhuǎn)測(cè)試頁面:
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();
}
}
②增加簡(jiǎn)單的長(zhǎng)按編輯頁面
創(chuàng)建卡片編輯 Ability(LevelCardConfigAbility),點(diǎn)擊 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”
污水液位計(jì)卡片編輯頁面詳細(xì)開發(fā)請(qǐng)看下面。
③數(shù)據(jù)手動(dòng)刷新
1. 在卡片編輯小卡片添加手動(dòng)刷新事件
index.hml:
index.json:
2. 創(chuàng)建 CardFormAbility(如果之前已經(jīng)創(chuàng)建過了就不用創(chuàng)建了)
在 src/main/config.json 中,如果你的小卡片是寫在 MainAbility 里面的,就不需要?jiǎng)?chuàng)建這個(gè) CardFormAbility,我是為了方便分開來,把卡片配置寫在 CardFormAbility 中。
因?yàn)槭茄菔敬a,所以請(qǐng)求后臺(tái)服務(wù)器獲得數(shù)據(jù)的代碼放在 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) {
// 解析收到的數(shù)據(jù)
ZSONObject messageJSON = ZSONObject.stringToZSON(message);
if(messageJSON.get(“message”).equals(“updata”)){ // 更新數(shù)據(jù)
ZSONObject zsonObject = new ZSONObject();
// 請(qǐng)求后臺(tái)服務(wù)器獲得數(shù)據(jù)
zsonObject.put(“l(fā)evel”, “100”);
FormBindingData formBindingData = new FormBindingData(zsonObject);
try {
// 更新數(shù)據(jù)
if (!updateForm(formId, formBindingData)) {
}
} catch (FormException e) {
e.printStackTrace();
}
}
super.onTriggerFormEvent(formId, message);
}
}
這樣點(diǎn)擊 index.hml 中的標(biāo)題,就可以更新數(shù)據(jù)了。
07
服務(wù)卡片進(jìn)階開發(fā)教程
①數(shù)據(jù)定時(shí)刷新
使用鴻蒙自帶的定時(shí)刷新:數(shù)據(jù)定時(shí)刷新需要在 src/main/config.json 配置文件中配置,是否開啟定時(shí)刷新和定時(shí)刷新的時(shí)間:
“updateEnabled”: true,
“updateDuration”: 1
編寫 CardFormAbility,重寫 onUpdateForm 方法:
@Overrideprotected void onUpdateForm(long formId) {
super.onUpdateForm(formId);
ZSONObject zsonObject = new ZSONObject();
zsonObject.put(“l(fā)evel”, “1.123”);
FormBindingData formBindingData = new FormBindingData(zsonObject);
// 調(diào)用updateForm接口去更新對(duì)應(yīng)的卡片,僅更新入?yún)⒅袛y帶的數(shù)據(jù)信息,其他信息保持不變
try {
if (!updateForm(formId, formBindingData)) {
// err process
}
} catch (FormException e) {
e.printStackTrace();
}
}
自定義刷新策略,請(qǐng)往下看。
②編輯頁面開發(fā)&編輯更新卡片邏輯開發(fā)
頁面開發(fā)
index.hml:
《div class=“container”》
《text class=“title”》
選擇液位計(jì)
《/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發(fā)送選擇的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#液位計(jì)’,
id: 1
}, {
title: ‘2#液位計(jì)’,
id: 2
},{
title: ‘3#液位計(jì)’,
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并進(jìn)行保存
IntentParams params = intent.getParams();
cardId = (long) params.getParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY);
super.onStart(intent);
}
@Override
public void onStop() {
super.onStop();
}
}
創(chuàng)建 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+“#液位計(jì):”);
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;
}
}
}
③自定義刷新策略
關(guān)系型數(shù)據(jù)庫加入包:在對(duì)應(yīng)的 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
重新構(gòu)建:創(chuàng)建數(shù)據(jù)庫類和表類。
數(shù)據(jù)庫類:例如,定義了一個(gè)數(shù)據(jù)庫類 LevelStore.java,數(shù)據(jù)庫包含了“Level”表,版本號(hào)為“1”。
數(shù)據(jù)庫類的 getVersion 方法和 getHelper 方法不需要實(shí)現(xiàn),直接將數(shù)據(jù)庫類設(shè)為虛類即可。
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 {
}
創(chuàng)建表類:
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 = “l(fā)evel”)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;
}
卡片數(shù)據(jù)將存在數(shù)據(jù)庫并定時(shí)刷新:
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);
// 卡片規(guī)格信息
int specificationId = (int) params.getParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY);
System.out.println( “創(chuàng)建卡片: ” + formId + “ ” + formName + “ ” + specificationId);
if(ormContext == null){
createDataBase(getContext());
}
// 存儲(chǔ)數(shù)據(jù)
Level newLevel = new Level(formId, formName);
boolean isSuccessed = ormContext.insert(newLevel);
isSuccessed = ormContext.flush();
}
public void createDataBase(Context context){
// 創(chuàng)建數(shù)據(jù)庫
ormContext = helper.getOrmContext(“LevelStore”, “LevelStore.db”, LevelStore.class);
// 啟動(dòng)定時(shí)刷新程序
startTimer();
}
private void startTimer(){
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 查詢數(shù)據(jù)庫獲取數(shù)據(jù)
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();
// 設(shè)置數(shù)據(jù)
double randomLevel = Math.random()*10;
DecimalFormat randomLevelDf = new DecimalFormat( “0.00”);
zsonObject.put(“l(fā)evel”, randomLevelDf.format(randomLevel));
FormBindingData formBindingData = new FormBindingData(zsonObject);
if (!updateForm(formId, formBindingData)) {
deleteLevelCard(formId);
}
}
} catch (FormException e) {
e.printStackTrace();
}
}
},5,700L);
}
責(zé)任編輯:haq
-
華為
+關(guān)注
關(guān)注
216文章
34417瀏覽量
251518 -
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2634瀏覽量
66308 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1974瀏覽量
30147
原文標(biāo)題:假如中國(guó)空間站用上鴻蒙系統(tǒng)...
文章出處:【微信號(hào):gh_834c4b3d87fe,微信公眾號(hào):OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論