????????6 月 2 日鴻蒙發(fā)布,今年的六月已經(jīng)被鴻蒙刷屏了。從安卓到鴻蒙,最直觀的變化應(yīng)該就是服務(wù)卡片了。我也是在學(xué)習(xí)鴻蒙的同時(shí),實(shí)際體驗(yàn)一下服務(wù)卡片的開(kāi)發(fā)。
接下來(lái)分享下我的制作過(guò)程,我使用的開(kāi)發(fā)環(huán)境是:
IDE:DevEco Studio 2.1 Release
軟件安裝和項(xiàng)目建立的部分就跳過(guò)了,相信大家都比較熟悉了。直奔主題服務(wù)卡片的制作。
服務(wù)卡片設(shè)計(jì)
首先要先了解服務(wù)卡片,都有哪些尺寸,支持哪些組件,使用什么語(yǔ)言。然后規(guī)劃好要實(shí)現(xiàn)哪些功能。
①尺寸規(guī)格
服務(wù)卡片有 4 種尺寸,分別是微卡片、小卡片、中卡片、大卡片。官方提供了 4 種基礎(chǔ)模板,12 種高級(jí)模板可以選擇。
基礎(chǔ)模板如下圖:
②功能設(shè)計(jì)
服務(wù)卡片設(shè)計(jì)的初衷就是信息顯示、服務(wù)直達(dá)。依照這個(gè)原則,我找了幾個(gè) Bilibili 中我比較常用的功能,來(lái)制作服務(wù)卡片,比如追番列表。
③開(kāi)發(fā)語(yǔ)言
看下表就一目了然了,就是推薦 JS。表格來(lái)源:
界面實(shí)現(xiàn)
本著學(xué)習(xí)的目的,卡片界面就不使用模板了。不過(guò)我們還是要通過(guò) IDE→File→New→Service Widget 來(lái)添加服務(wù)卡片,這樣添加 IDE 會(huì)自動(dòng)添加配置和管理相關(guān)文件。
然后服務(wù)卡片的界面重新編寫(xiě)。服務(wù)卡片常用的的容器組件有 div、list、stack、swiper 等。
我使用了 4 種尺寸的卡片,并盡可能的使用到所有的容器組件。
①div:基礎(chǔ)容器組件
就是用來(lái)劃分區(qū)域的。比較常用。比如追番服務(wù)卡片。
代碼如下:
《div class=“div_root” 》《!--在服務(wù)卡片設(shè)置一個(gè) 根div 橫向布局--》
《div class=“div_container”》《!--在根div 橫向放置4個(gè)div,每個(gè)div內(nèi)部從上往下排列--》
《image class=“item_image” src=“{{ src1 }}” onclick=“routerEvent1”》《/image》
《text class=“item_title”》{{ itemTitle1 }}《/text》
《text class=“item_content”》{{ itemContent1 }}《/text》
《/div》
《div class=“div_container”》《!--第二列--》
《image class=“item_image” src=“{{ src2 }}” onclick=“routerEvent2”》《/image》
《text class=“item_title”》{{ itemTitle2 }}《/text》
《text class=“item_content”》{{ itemContent2 }}《/text》
《/div》
《div class=“div_container”》《!--第三列--》
《image class=“item_image” src=“{{ src3 }}” onclick=“routerEvent3”》《/image》
《text class=“item_title”》{{ itemTitle3 }}《/text》
《text class=“item_content”》{{ itemContent3 }}《/text》
《/div》
《div class=“div_container”》《!--第四列--》
《image class=“item_image” src=“{{ src4 }}” onclick=“routerEvent4”》《/image》
《text class=“item_title”》{{ itemTitle4 }}《/text》
《text class=“item_content”》{{ itemContent4 }}《/text》
《/div》
《/div》
.div_root {
flex-direction: row; /*flex容器主軸方向,row:水平方向從左到右。*/
justify-content: center; /*flex容器當(dāng)前行的主軸對(duì)齊格式,center:項(xiàng)目位于容器的中心。*/
margin:6px; /*外邊距屬性:只有一個(gè)值時(shí),這個(gè)值會(huì)被指定給全部的四個(gè)邊。*/
border-radius: 10px; /*設(shè)置元素的外邊框圓角半徑。*/
}
.div_container {
flex-direction: column; /*flex容器主軸方向,column:垂直方向從上到下。*/
justify-content: flex-start; /*flex容器當(dāng)前行的主軸對(duì)齊格式,flex-start:項(xiàng)目位于容器的開(kāi)頭。*/
margin:6px;
}
.item_image {
height: 60%; /*卡片在不同設(shè)備,尺寸會(huì)發(fā)生變化,所以最好使用百分比進(jìn)行標(biāo)注。*/
border-radius: 10px;
background-color: #F1F3F5; /*設(shè)置背景顏色。*/
}
@media (dark-mode: true) { /*當(dāng)前系統(tǒng)為深色模式時(shí),使用這里的配置,如果沒(méi)有顏色設(shè)置,可以不設(shè)置*/
.item_image {
height: 60%;
border-radius: 10px;
background-color: #202224;
}
}
.item_title {
margin-top: 10px; /*設(shè)置上邊距。*/
font-size: 12px; /*設(shè)置文本的尺寸。*/
font-weight: bold; /*設(shè)置文本的字體粗細(xì)。取值[100, 900],默認(rèn)為400。*/
max-lines:1; /*設(shè)置文本的最大行數(shù)。*/
text-overflow: ellipsis; /*根據(jù)父容器大小顯示,顯示不下的文本用省略號(hào)代替。需配合max-lines使用。*/
color: #e5000000; /*設(shè)置文本的顏色。*/
}
.item_content {
margin-top: 5px;
font-size: 9px;
font-weight: bold;
text-overflow: ellipsis;
max-lines:1;
color: #99000000;
}
Note:其實(shí)這個(gè)服務(wù)卡片的布局,每一列的內(nèi)容都是相同的,是應(yīng)該使用 list 組件的。
②list:列表容器組件
就如上面所說(shuō)的連續(xù)相同的部分,可以使用這個(gè)組件,List 不但可以顯示更多的內(nèi)容,而且代碼更少。
代碼如下:
《list class=“l(fā)ist”》
《list-item for=“{{cards}}” class=“l(fā)ist-item”》
《div class=“div” onclick=“sendRouteEvent”》
《image class=“item_image” src=“{{ $item.pic }}”》《/image》
《text class=“item_name”》{{ $item.name }}《/text》
《text class=“item_title”》{{ $item.title }}《/text》
《/div》
《/list-item》
《/list》
.list{
align-items:center; /*list每一列交叉軸上的對(duì)齊格式:元素在交叉軸居中*/
}
.list-item{
border-radius: 15px;
background-color: #f2f2f2;
margin-bottom: 5px;
}
.div{
flex-direction: column;
}
.item_image {
border-top-right-radius: 15px;
border-top-left-radius: 15px;
}
.item_name {
margin:5px 8px 0px;
font-size: 12px;
color: #262626;
}
.item_title{
margin:3px 8px 8px;
font-size: 10px;
color: #AAAAAA;
max-lines: 2;
text-overflow: ellipsis; /* 省略號(hào) */
}
③stack:堆疊容器組件
簡(jiǎn)單來(lái)說(shuō)就是可以在一張圖片上堆疊顯示另一張圖片,例如下圖藍(lán)框的圖片覆蓋在紅框圖片的上面。
④swiper:滑動(dòng)容器組件
正常情況下 swiper 是可以實(shí)現(xiàn)上下、左右滑動(dòng)操作的。但是放置在桌面上的服務(wù)卡片,在左右滑動(dòng)操作的時(shí)候,會(huì)使系統(tǒng)分不清楚用戶(hù)是要左右滑動(dòng)屏幕,還是左右滑動(dòng)卡片。
所以目前服務(wù)卡片的 swiper 容器是不支持手勢(shì)滑動(dòng)切換子組件的。下圖是通過(guò)點(diǎn)擊圖片側(cè)面的控制條實(shí)現(xiàn)上下滑動(dòng)的。
但是我個(gè)人覺(jué)得上下滑動(dòng)其實(shí)還是挺好用的,畢竟在 list 組件上是可以上下滑動(dòng)的,只可惜目前還不支持。
總結(jié):服務(wù)卡片的設(shè)計(jì)比較簡(jiǎn)單,零基礎(chǔ)也沒(méi)關(guān)系,官方還貼心的準(zhǔn)備了模板。只要挑選模板,設(shè)置變量也能快速構(gòu)建。
API 數(shù)據(jù)請(qǐng)求
卡片設(shè)計(jì)好之后,就需要通過(guò) Bilibili 的 API 來(lái)獲取數(shù)據(jù)了。主要就是給權(quán)限添加依賴(lài),然后發(fā)送網(wǎng)絡(luò)請(qǐng)求,通過(guò) API 獲取 JSON 的返回值,然后解析 JSON 得到我們需要的數(shù)據(jù)。
①添加聯(lián)網(wǎng)權(quán)限
要在 config.json 配置文件的 module 中添加:“reqPermissions”: [{“name”:“ohos.permission.INTERNET”}]。
{
。。。 。。。
“module”: {
。。。 。。。
“reqPermissions”: [{“name”:“ohos.permission.INTERNET”}]
}
}
②添加依賴(lài)包
找到 entry/build.gradle 文件,在 dependencies 下添加:
dependencies {
implementation fileTree(dir: ‘libs’, include: [‘*.jar’, ‘*.har’])
testImplementation ‘junit4.13’
ohosTestImplementation ‘com.huawei.ohos.testkit1.0.0.100’
// ZZRHttp 可以單獨(dú)一個(gè)進(jìn)程進(jìn)行http請(qǐng)求
implementation ‘com.zzrv5.zzrhttp1.0.1’
// fastjson 可以解析JSON格式
implementation group: ‘com.alibaba’, name: ‘fastjson’, version: ‘1.2.75’
}
③http 請(qǐng)求
以獲取粉絲數(shù)為例,如果在瀏覽器中輸入 https://api.bilibili.com/x/relation/stat?vmid=383565952。
其中 vmid:是要查詢(xún)的用戶(hù) ID,follower 的值就是粉絲數(shù)。
網(wǎng)絡(luò)訪問(wèn)我們可以使用 HttpURLConnection,或者 okhttp 等依賴(lài)包,但是需要開(kāi)啟子線程、處理異常等操作,所以這里使用的是 ZZR 老師封裝好的 ZZRHttp。
代碼實(shí)現(xiàn):
//獲取Bilibili粉絲數(shù),這里就要用到第二步我們添加的ZZRHttp
String url = “https://api.bilibili.com/x/relation/stat?vmid=383565952”;
ZZRHttp.get(url, new ZZRCallBack.CallBackString() {
@Override
public void onFailure(int i, String s) {
HiLog.info(TAG, “API返回失敗”);
}
@Override
public void onResponse(String s) {
HiLog.info(TAG, “API返回成功”);
// 如果返回成功,返回的結(jié)果就會(huì)保存在 String s 中。
// s = {“code”:0,“message”:“0”,“ttl”:1,“data”:{“mid”:383565952,“following”:70,“whisper”:0,“black”:0,“follower”:5384}}
}
});
④解析 JSON
得到的是 JSON 格式的返回值,要得到 follower 的值,還需要對(duì) JSON 進(jìn)行數(shù)據(jù)解析。
先按照 JSON 的內(nèi)容,生成 JAVA 類(lèi)??梢宰约簩?xiě),也可以百度搜 ”JSON 生成 Java 實(shí)體類(lèi)“,可直接生成。
代碼如下:
public class BilibiliFollower {
public static class Data{
private int follower;
public int getFollower() {
return follower;
}
public void setFollower(int follower) {
this.follower = follower;
}
}
private BilibiliFollower.Data data;
public BilibiliFollower.Data getData() {
return data;
}
public void setData(BilibiliFollower.Data data) {
this.data = data;
}
}
//解析JSON,使用第二步我們添加的fastjson包try {
//1.調(diào)用fastjson解析,結(jié)果保存在JSON對(duì)應(yīng)的類(lèi)
BilibiliFollower bilibiliFollower = JSON.parseObject(s,BilibiliFollower.class);
//2.get方法獲取解析內(nèi)容
BilibiliFollower.Data data= bilibiliFollower.getData();
System.out.println(“解析成功”+data.getFollower());
} catch (Exception e) {
HiLog.info(TAG, “解析失敗”);
}
總結(jié):一定要添加聯(lián)網(wǎng)權(quán)限不然是獲取不到數(shù)據(jù)的。添加了 2 個(gè)依賴(lài)包,可以很方便的提取數(shù)據(jù)。
獲取其他的卡片數(shù)據(jù)的方式同理,不過(guò)代碼比較多,就不一一展示了,感興趣可以下載全量代碼看。
數(shù)據(jù)更新
要想將數(shù)據(jù)更新到服務(wù)卡片,得先了解服務(wù)卡片的運(yùn)作機(jī)制。如果是通過(guò) IDE→File→New→Service Widget 添加的服務(wù)卡片,那么在 MainAbility 中會(huì)添加卡片的生命周期回調(diào)方法。
參考下面的代碼:
public class MainAbility extends Ability {
。。。 。。。
protected ProviderFormInfo onCreateForm(Intent intent) {。。。}//在服務(wù)卡片上右擊》》服務(wù)卡片(或上滑)時(shí),通知接口
protected void onUpdateForm(long formId) {。。。}//在服務(wù)卡片請(qǐng)求更新,定時(shí)更新時(shí),通知接口
protected void onDeleteForm(long formId) {。。}//在服務(wù)卡片被刪除時(shí),通知接口
protected void onTriggerFormEvent(long formId, String message) {。。。}//JS服務(wù)卡片click時(shí),通知接口
}
①定時(shí)更新
按照上述分析,我們只需要在 config.json 中開(kāi)啟服務(wù)卡片的周期性更新,在 onUpdateForm(long formId) 方法下執(zhí)行數(shù)據(jù)獲取更新。
config.json 文件“abilities”的 forms 模塊配置細(xì)節(jié)如下:
“forms”: [
{
“jsComponentName”: “widget2”,
“isDefault”: true,
“scheduledUpdateTime”: “10:30”,//定點(diǎn)刷新的時(shí)刻,采用24小時(shí)制,精確到分鐘?!皍pdateDuration”: 0時(shí),才會(huì)生效。
“defaultDimension”: “1*2”,
“name”: “widget2”,
“description”: “This is a service widget”,
“colorMode”: “auto”,
“type”: “JS”,
“supportDimensions”: [
“1*2”
],
“updateEnabled”: true, //表示卡片是否支持周期性刷新
“updateDuration”: 1 //卡片定時(shí)刷新的更新周期,1為30分鐘,2為60分鐘,N為30*N分鐘
}
]
這樣結(jié)合我們?cè)谏弦徊将@取 API 數(shù)據(jù),解析 JSON,開(kāi)啟服務(wù)卡片的周期性更新,就可以在 updateFormData() 實(shí)現(xiàn)服務(wù)卡片的數(shù)據(jù)更新了。
截取 follower 數(shù)據(jù)更新的部分代碼如下:
public void updateFormData(long formId, Object.。。 vars) {
HiLog.info(TAG, “update form data: formId” + formId);
//這部分用來(lái)獲取粉絲數(shù)
String url = “https://api.bilibili.com/x/relation/stat?vmid=383565952”;
ZZRHttp.get(url, new ZZRCallBack.CallBackString() {
@Override
public void onFailure(int i, String s) {HiLog.info(TAG, “API返回失敗”);}
@Override
public void onResponse(String s) {
HiLog.info(TAG, “API返回成功”);
try {
//1.調(diào)用fastjson解析,結(jié)果保存在JSON對(duì)應(yīng)的類(lèi)
BilibiliFollower bilibiliFollower = JSON.parseObject(s,BilibiliFollower.class);
//2.get方法獲取解析內(nèi)容
BilibiliFollower.Data data= bilibiliFollower.getData();
System.out.println(“解析成功”+data.getFollower());
//這部分用來(lái)更新卡片信息
ZSONObject zsonObject = new ZSONObject(); //1.將要刷新的數(shù)據(jù)存放在一個(gè)ZSONObject實(shí)例中
zsonObject.put(“follower”,data.getFollower()); //2.更新數(shù)據(jù),data.getFollower()就是在API數(shù)據(jù)請(qǐng)求中獲取的粉絲數(shù)。
FormBindingData formBindingData = new FormBindingData(zsonObject); //3.將其封裝在一個(gè)FormBindingData的實(shí)例中
try {
((MainAbility)context).updateForm(formId,formBindingData); //4.調(diào)用MainAbility的方法updateForm(),并將formBindingData作為第二個(gè)實(shí)參
} catch (FormException e) {
e.printStackTrace();
HiLog.info(TAG, “更新卡片失敗”);
}
} catch (Exception e) {
HiLog.info(TAG, “解析失敗”);
}
}
});
}
②手動(dòng)更新
正常來(lái)說(shuō)這樣就可以正常更新數(shù)據(jù)了,但是會(huì)有個(gè)問(wèn)題。就是在服務(wù)卡片首次創(chuàng)建添加到桌面的時(shí)候,在添加完的至少 30 分鐘里,數(shù)據(jù)是不會(huì)更新的。
此時(shí)如果在 index.json 中設(shè)置初始信息,那么在添加完成的前 30 分鐘數(shù)據(jù)都是寫(xiě)死在 data 中的。如果不設(shè)置初始信息那么卡片就是空白的。
所以按照前面服務(wù)卡片的運(yùn)作機(jī)制的分析,我們還需要在卡片初始化 onCreateForm() 的時(shí)候進(jìn)行一次更新。
這個(gè)非常簡(jiǎn)單用 onCreateForm() 調(diào)用 onUpdateForm(formId) 即可。
@Overrideprotected ProviderFormInfo onCreateForm(Intent intent) {
。。。 。。。
//初始化時(shí)先在線更新一下卡片
onUpdateForm(formId);
return formController.bindFormData();
}
總結(jié):這里的 onUpdateForm(formId) 中 API 的網(wǎng)絡(luò)請(qǐng)求一定要新開(kāi)一個(gè)子線程,不然會(huì)影響頁(yè)面加載。
這也是前面說(shuō)的用 ZZRhttp 的原因。不過(guò)現(xiàn)在也遇到一個(gè)問(wèn)題,當(dāng)卡片數(shù)量變多時(shí),同時(shí)在線更新這么多的卡片會(huì)變得非常緩慢,這個(gè)問(wèn)題還有待解決。
功能直達(dá)
目前服務(wù)卡片僅支持 click 通用事件,事件類(lèi)型:跳轉(zhuǎn)事件(router)和消息事件(message)。
詳細(xì)說(shuō)明參考官方文檔:
https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-service-widget-syntax-hml-0000001152828575
①跳轉(zhuǎn)事件
接下來(lái)實(shí)現(xiàn)與服務(wù)卡片的交互,當(dāng)點(diǎn)擊服務(wù)卡片時(shí),會(huì)跳轉(zhuǎn)到相應(yīng)的頁(yè)面,所以這里使用跳轉(zhuǎn)事件。
以番劇更新的卡片為例:
首先我們要先添加一個(gè)要跳轉(zhuǎn)的頁(yè)面。如下圖所示添加一個(gè) Page Ability,比如:VideoSlice。
新建完成之后會(huì)增加 VideoSlice 和 slice/VideoSliceSlice 兩個(gè)文件,和 base/layout/ability_bilibili_page.xml 頁(yè)面文件。
@Overridepublic void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_video);
Text text = (Text) findComponentById(ResourceTable.Id_text);
text.setText(“頁(yè)面跳轉(zhuǎn)中”);
// 隨機(jī)圖片數(shù)組
int[] resource = {ResourceTable.Media_36e,ResourceTable.Media_36g,ResourceTable.Media_36h,ResourceTable.Media_38p};
Component component = findComponentById(ResourceTable.Id_image);
if (component instanceof Image) {
Image image = (Image) component;
image.setPixelMap(resource[(int)(Math.random()*3)]);//隨機(jī)顯示一張圖片
}
String url = “https://m.bilibili.com”;
String param = intent.getStringParam(“params”);//從intent中獲取 跳轉(zhuǎn)事件定義的params字段的值
if(param !=null){
ZSONObject data = ZSONObject.stringToZSON(param);
url = data.getString(“url”);
}
webview(url);
}
//啟動(dòng)webviewpublic void webview(String url){
WebView webView = (WebView) findComponentById(ResourceTable.Id_webview);
webView.getWebConfig().setJavaScriptPermit(true); // 如果網(wǎng)頁(yè)需要使用JavaScript,增加此行;如何使用JavaScript下文有詳細(xì)介紹
webView.load(url);
}
增加 webview,將頁(yè)面默認(rèn)的 Text 控件修改為 webview。
《?xml version=“1.0” encoding=“utf-8”?》《DirectionalLayout
xmlns:ohos=“http://schemas.huawei.com/res/ohos”
ohos:height=“match_parent”
ohos:width=“match_parent”
ohos:alignment=“center”
ohos:orientation=“vertical”》
《ohos.agp.components.webengine.WebView
ohos:id=“$+id:webview”
ohos:height=“match_parent”
ohos:width=“match_parent”》
《/ohos.agp.components.webengine.WebView》《/DirectionalLayout》
在 index.hml 中給要觸發(fā)的控件上添加 onclick,比如:onclick=“routerEvent1”。
《div class=“div_root” 》《!--在服務(wù)卡片設(shè)置一個(gè) 根div 橫向布局--》
《div class=“div_container”》《!--在根div 橫向放置4個(gè)div,每個(gè)div內(nèi)部從上往下排列--》
《image class=“item_image” src=“{{ src1 }}” onclick=“routerEvent1”》《/image》
《text class=“item_title”》{{ itemTitle1 }}《/text》
《text class=“item_content”》{{ itemContent1 }}《/text》
《/div》
。。。 。。。
《/div》
在 index.json 中,添加對(duì)應(yīng)的 actions,跳轉(zhuǎn)事件要多加一個(gè)參數(shù)“abilityName”,指定要跳轉(zhuǎn)的頁(yè)面,并且攜帶參數(shù) url。
{
“data”: {
},
“actions”: {
“routerEvent1”: {
“action”: “router”,
“bundleName”: “com.liangzili.servicewidget”,
“abilityName”: “com.liangzili.servicewidget.VideoSlice”,
“params”: {
“url”: “{{url1}}”
}
},
“routerEvent2”: {
。。。 。。。
}
②消息事件
這里使用視頻動(dòng)態(tài)服務(wù)卡片,做一個(gè)消息事件的測(cè)試,效果如下圖,點(diǎn)擊左右邊,實(shí)現(xiàn)服務(wù)卡片的滑動(dòng)。
在小卡片上這樣的操作體驗(yàn)不好。所以消息事件中的例子,只是為了測(cè)試,并沒(méi)有加到項(xiàng)目里。
在 index.hml 中給要觸發(fā)的控件上添加 onclick,比如:onclick=“sendMessageEvent”。
《-- 為了方便測(cè)試,直接將onclick添加在左右兩側(cè)的div組件上 --》《div class=“div” onclick=“sendMessageEvent0”》
《image class=“item_image” src=“{{ src0 }}”》《/image》
《text class=“item_title”》{{ itemTitle0 }}《/text》
《text class=“item_content”》{{ itemContent0 }}《/text》《/div》《div class=“div” onclick=“sendMessageEvent1”》
《image class=“item_image” src=“{{ src1 }}”》《/image》
《text class=“item_title”》{{ itemTitle1 }}《/text》
《text class=“item_content”》{{ itemContent1 }}《/text》《/div》
在 index.json 中,添加對(duì)應(yīng)的 actions。
{
“data”: {
},
“actions”: {
“sendMessageEvent0”: {
“action”: “message”,
“params”: {
“p1”: “l(fā)eft”,
“index”: “{{index}}”
}
},
“sendMessageEvent1”: {
“action”: “message”,
“params”: {
“p1”: “right”,
“index”: “{{index}}”
}
}
}
}
如果是消息事件(message)當(dāng)點(diǎn)擊帶有 onclick 的控件時(shí),會(huì)觸發(fā) MainAbility 下的這個(gè)函數(shù)。
@Overrideprotected void onTriggerFormEvent(long formId, String message) {
HiLog.info(TAG, “onTriggerFormEvent: ” + message); //params的內(nèi)容就通過(guò)message傳遞過(guò)來(lái)
super.onTriggerFormEvent(formId, message);
FormControllerManager formControllerManager = FormControllerManager.getInstance(this);
FormController formController = formControllerManager.getController(formId);//通過(guò)formId得到卡片控制器
formController.onTriggerFormEvent(formId, message);//接著再調(diào)用,對(duì)應(yīng)的控制器 WidgetImpl
}
最后調(diào)用卡片控制器 WidgetImpl 中的 onTriggerFormEvent()。
public void onTriggerFormEvent(long formId, String message) {
HiLog.info(TAG, “onTriggerFormEvent.”+message);
//先獲取message中的參數(shù)
ZSONObject data = ZSONObject.stringToZSON(message);
String p1 = data.getString(“p1”);
Integer index = data.getIntValue(“index”);
ZSONObject zsonObject = new ZSONObject(); //將要刷新的數(shù)據(jù)存放在一個(gè)ZSONObject實(shí)例中
Integer indexMax = 2; //有N個(gè)滑塊組件就設(shè)置N-1
if(p1.equals(“right”)){ //判斷點(diǎn)擊方向,如果是右側(cè)
if(index == indexMax){index = -1;} //實(shí)現(xiàn)循環(huán)滾動(dòng)
index = index+1;
zsonObject.put(“index”,index);
}else { //判斷點(diǎn)擊方向,如果是左側(cè)
if(index == 0){index = indexMax+1;} //實(shí)現(xiàn)循環(huán)滾動(dòng)
index = index-1;
zsonObject.put(“index”,index);
}
FormBindingData formBindingData = new FormBindingData(zsonObject);
try {
((MainAbility)context).updateForm(formId,formBindingData);
} catch (FormException e) {
e.printStackTrace();
HiLog.info(TAG, “更新卡片失敗”);
}
}
③list 跳轉(zhuǎn)事件
list 組件只能添加一個(gè) onclick,而且在點(diǎn)擊的同時(shí)還需要獲取點(diǎn)擊的是list列表中的哪一項(xiàng),這個(gè)比較特殊。
《list class=“l(fā)ist” else》
《list-item for=“{{list}}” class=“l(fā)ist-item”》
《div class=“div” onclick=“sendRouteEvent”》
。。。 。。。
《/div》
《/list-item》
《/list》
這個(gè)坑折磨了我好久,最終我發(fā)現(xiàn)在 index.json 中,可以使用 item,item,idx 獲取到 hml 頁(yè)面 list 的元素變量和索引。
但是在官方文檔并沒(méi)有找到相關(guān)的內(nèi)容,嘗試了很久才解決這個(gè)問(wèn)題。之后的部分就和跳轉(zhuǎn)事件一樣了,使用 Video 頁(yè)面解析 url 進(jìn)行播放就可以了。
“actions”: {
“sendRouteEvent”: {
“action”: “router”,
“bundleName”: “com.liangzili.demos”,
“abilityName”: “com.liangzili.demos.Video”,
“params”: {
“url”: “{{$item.short_url}}”,
“index”: “{{$idx}}”
}
}
}
總結(jié):解決了 list 的點(diǎn)擊事件之后,才發(fā)現(xiàn)這歌控件真是好用。能用 list 還是 list 方便。
加載頁(yè)面,保存 Cookie
啟動(dòng)之后的頁(yè)面主要是為了登錄賬號(hào),因?yàn)榇蟛糠值?API 是需要登錄之后才可以獲取到的。
①webview 加載頁(yè)面
在 base/layout/ability_main.xml 中添加 webview 組件,代碼如下:
《ohos.agp.components.webengine.WebView
ohos:id=“$+id:webview”
ohos:height=“match_parent”
ohos:width=“match_parent”》
《/ohos.agp.components.webengine.WebView》
然后在啟動(dòng)頁(yè)面執(zhí)行加載操作。但其實(shí)加載前需要先從數(shù)據(jù)庫(kù)中提取 cookie 信息,這個(gè)接下來(lái)說(shuō)。
String url = “https://m.bilibili.com”;
WebView webView = (WebView) findComponentById(ResourceTable.Id_webview);
webView.getWebConfig().setJavaScriptPermit(true); // 如果網(wǎng)頁(yè)需要使用JavaScript,增加此行;如何使用JavaScript下文有詳細(xì)介紹
webView.load(url);
②Cookie 的讀取和保存類(lèi)
com/liangzili/demos/utils/CookieUtils.java:
public class CookieUtils {
private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG,0x0,CookieUtils.class.getName());
/**
* 使用關(guān)系型數(shù)據(jù)庫(kù)[讀?。軨ookie
* @param preferences
* @param url
*/
public static void ExtarctCookie(Preferences preferences, String url){
Map《String, ?》 map = new HashMap《》();
//先從數(shù)據(jù)庫(kù)中取出cookie
map = PreferenceDataBase.GetCookieMap(preferences);
//然后寫(xiě)入到cookieStore
CookieStore cookieStore = CookieStore.getInstance();//1.獲取一個(gè)CookieStore的示例
for (Map.Entry《String, ?》 entry : map.entrySet()) {
HiLog.info(TAG,entry.getKey()+“=”+entry.getValue().toString());
cookieStore.setCookie(url,entry.getKey()+“=”+entry.getValue().toString());//2.寫(xiě)入數(shù)據(jù),只能一條一條寫(xiě)
}
}
/**
* 使用關(guān)系型數(shù)據(jù)庫(kù)[保存]Cookie
* @param preferences 數(shù)據(jù)庫(kù)的Preferences實(shí)例
* @param url 指定Cookie對(duì)應(yīng)的域名
*/
public static void SaveCookie(Preferences preferences,String url){
//先取出要保存的cookie
CookieStore cookieStore = CookieStore.getInstance();
String cookieStr = cookieStore.getCookie(url);
HiLog.info(TAG,“saveCookie(String url)”+url+cookieStr);
//然后將cooke轉(zhuǎn)成map
Map《String,String》 cookieMap = cookieToMap(cookieStr);
//最后將map寫(xiě)入數(shù)據(jù)庫(kù)
PreferenceDataBase.SaveMap(preferences,cookieMap);
}
// cookieToMap
public static Map《String,String》 cookieToMap(String value) {
Map《String, String》 map = new HashMap《String, String》();
value = value.replace(“ ”, “”);
if (value.contains(“;”)) {
String values[] = value.split(“;”);
for (String val : values) {
String vals[] = val.split(“=”);
map.put(vals[0], vals[1]);
}
} else {
String values[] = value.split(“=”);
map.put(values[0], values[1]);
}
return map;
}
}
偏好型數(shù)據(jù)庫(kù)
數(shù)據(jù)庫(kù)的操作主要是 com/liangzili/demos/database/PreferenceDataBase.java 這個(gè)類(lèi)。使用輕量級(jí)偏好型數(shù)據(jù)庫(kù),更符合我們這里的需求。
①獲取 Preferences 實(shí)例
public class PreferenceDataBase {
private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG,0x0,PreferenceDataBase.class.getName());
/**
* 獲取Preferences實(shí)例
* @param context 數(shù)據(jù)庫(kù)文件將存儲(chǔ)在由context上下文指定的目錄里。
* @param name fileName表示文件名,其取值不能為空,也不能包含路徑
* @return //返回對(duì)應(yīng)數(shù)據(jù)庫(kù)的Preferences實(shí)例
*/
public static Preferences register(Context context,String name) {
DatabaseHelper databaseHelper = new DatabaseHelper(context);
Preferences preferences = databaseHelper.getPreferences(name);
return preferences;
}
。。。 。。。
}
②從數(shù)據(jù)庫(kù)中保存和讀取 Map
/**
* Map[保存]到偏好型數(shù)據(jù)庫(kù)
* @param preferences 數(shù)據(jù)庫(kù)的Preferences實(shí)例
* @param map 要保存的map
*/
public static void SaveMap(Preferences preferences,Map《String,String》 map){
// 遍歷map
for (Map.Entry《String, String》 entry : map.entrySet()) {
HiLog.info(TAG,entry.getKey() + “=” + entry.getValue());
preferences.putString(entry.getKey(),entry.getValue());//3.將數(shù)據(jù)寫(xiě)入Preferences實(shí)例,
}
preferences.flushSync();//4.通過(guò)flush()或者flushSync()將Preferences實(shí)例持久化。
}
/**
* 從偏好型數(shù)據(jù)庫(kù)[讀取]Map
* @param preferences 數(shù)據(jù)庫(kù)的Preferences實(shí)例
* @return 要讀取的map
*/
public static Map《String,?》 GetCookieMap(Preferences preferences){
Map《String, ?》 map = new HashMap《》();
map = preferences.getAll();//3.讀取數(shù)據(jù)
return map;
}
③提取某些 Cookie 的值
/**
* 獲取Cookie中的SESSDATA值
* @param context 上下文用來(lái)指定數(shù)據(jù)文件存儲(chǔ)路徑
* @return Cookie中的SESSDATA值
*/
public static String getSessData(Context context){
// 開(kāi)啟數(shù)據(jù)庫(kù)
DatabaseHelper databaseHelper = new DatabaseHelper(context);//1.創(chuàng)建數(shù)據(jù)庫(kù)使用數(shù)據(jù)庫(kù)操作的輔助類(lèi)
Preferences preferences = databaseHelper.getPreferences(“bilibili”);//2.獲取到對(duì)應(yīng)文件名的Preferences實(shí)例,filename是String類(lèi)型
String SESSDATA = preferences.getString(“SESSDATA”,“”); //3.讀取數(shù)據(jù)
return SESSDATA;
}
/**
* 獲取Cookie中的Vmid值
* @param context
* @return Cookie中的Vmid值
*/
public static String getVmid(Context context){
// 開(kāi)啟數(shù)據(jù)庫(kù)
DatabaseHelper databaseHelper = new DatabaseHelper(context);//1.創(chuàng)建數(shù)據(jù)庫(kù)使用數(shù)據(jù)庫(kù)操作的輔助類(lèi)
Preferences preferences = databaseHelper.getPreferences(“bilibili”);//2.獲取到對(duì)應(yīng)文件名的Preferences實(shí)例,filename是String類(lèi)型
String DedeUserID = preferences.getString(“DedeUserID”,“”); //3.讀取數(shù)據(jù)
return DedeUserID;
}
編輯:jq
-
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7035瀏覽量
89047 -
代碼
+關(guān)注
關(guān)注
30文章
4788瀏覽量
68628 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2352瀏覽量
42863
原文標(biāo)題:我為B站添加鴻蒙服務(wù)卡片!
文章出處:【微信號(hào):Huawei_Kirin,微信公眾號(hào):華為麒麟】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論