0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何在鴻蒙系統(tǒng)弄一個(gè)彩票查詢卡片

OpenHarmony技術(shù)社區(qū) ? 來(lái)源:HarmonyOS技術(shù)社區(qū) ? 作者:中軟國(guó)際 ? 2021-09-06 09:17 ? 次閱讀

接觸鴻蒙開發(fā)已經(jīng)有 3 個(gè)來(lái)月了,最近開始在看鴻蒙卡片開發(fā)。因?yàn)橹暗拈_發(fā)大都是基于 Java UI,但按官方的說(shuō)法,JS 卡片相比 Java 卡片有更大的優(yōu)勢(shì),故決定寫個(gè) JS 卡片的 demo 來(lái)練練手。

碰巧,前幾天和媳婦兒在散步時(shí)撿到 1 元錢,沒能交給警察叔叔,媳婦兒就提議“我們把它昧了吧,買張彩票?!?/p>

由于不是老 CM,沒有關(guān)注開獎(jiǎng)的習(xí)慣,想著要是能把開獎(jiǎng)結(jié)果放在手機(jī)桌面顯示就好了,這樣就不會(huì)錯(cuò)過我一夜暴富的機(jī)會(huì)了。有了需求就開擼,然后就有了這篇文章!

項(xiàng)目簡(jiǎn)介

①本項(xiàng)目基于 API 6 開發(fā),demo 運(yùn)行在 API 6 以下的手機(jī)上會(huì)出現(xiàn)部分功能不能使用的現(xiàn)象。

②卡片功能上實(shí)現(xiàn)了***、福彩 3D 的最近一期開獎(jiǎng)結(jié)果查詢,點(diǎn)擊卡片“刷新”按鈕,調(diào)用接口更新最新數(shù)據(jù);點(diǎn)擊“查看更多”按鈕,跳轉(zhuǎn)至應(yīng)用主界面。

③應(yīng)用主界面上實(shí)現(xiàn)了***、福彩 3D 近 50 期開獎(jiǎng)結(jié)果查看。

④以上數(shù)據(jù)均使用了聚合數(shù)據(jù)的 https://www.juhe.cn/docs/api/id/300 免費(fèi)接口。

需申請(qǐng) key,一個(gè) key 一天可免費(fèi)調(diào)用 100 次,如遇 key使 用次數(shù)過多導(dǎo)致接口請(qǐng)求失敗情況時(shí),開發(fā)者可自行申請(qǐng) key 并替換 Constants.java 文件下的 JH_KEY 常量值),數(shù)據(jù)可能會(huì)有延遲。

⑤卡片開發(fā)部分使用了卡片的 JS UI 框架,但由于系統(tǒng) PA 與 FA 相互調(diào)用的限制問題,卡片的業(yè)務(wù)邏輯部分仍然采用 Java 代碼編寫。

PS:最開始的想法是盡量可能的少依賴 Java 代碼,故嘗試了 JS FA 與 Java PA 相互調(diào)用的方式,但當(dāng)應(yīng)用進(jìn)程被干掉時(shí),Java 端無(wú)法再調(diào)用到 JS 端方法。

這樣就導(dǎo)致 JS 只能寫 UI 部分,業(yè)務(wù)邏輯還得 Java 層實(shí)現(xiàn)。有知道解決辦法的也麻煩告知一聲。

⑥卡片業(yè)務(wù)層使用 Java 開發(fā),采用了簡(jiǎn)單的 MVP 架構(gòu),網(wǎng)絡(luò)請(qǐng)求和數(shù)據(jù)處理部分使用了 rxjava3+retrofit 框架。

⑦應(yīng)用主界面使用了 JS-UI 框架實(shí)現(xiàn)。

實(shí)現(xiàn)效果

請(qǐng)忽略我粗陋的 UI 設(shè)計(jì)和 GIF 的渣渣像素。

項(xiàng)目代碼結(jié)構(gòu)分析

base:

IBasePresenter:MVP 架構(gòu)中 presenter 基類接口

IBaseView:MVP 架構(gòu)中 view 基類接口

network:

bean,LotteryBean:彩票詳情接口返回對(duì)應(yīng) model

CachedLotteryDetailUtil:彩票詳情接口請(qǐng)求工具類,主要作用是防止重復(fù)調(diào)用詳情接口

LogInterceptor:OKhttp 日志攔截工具類

LotteryAPI:接口請(qǐng)求類,通過 retrofit 注解,將接口返回?cái)?shù)據(jù)轉(zhuǎn)化為實(shí)體類

Services:配置 Retrofit 并提供

presenter:

IMainContract:MVP 中 view 與 presenter 的橋梁

MainPresenter:提供彩票詳情接口的請(qǐng)求,并處理接口返回?cái)?shù)據(jù)為卡片需要的 ZSONObject 對(duì)象

utils:

LogUtil:日志打印工具類

widget:

controller:FormController:創(chuàng)建卡片時(shí)自動(dòng)生成,卡片管理器的抽象基類;FormControllerManager:創(chuàng)建卡片時(shí)自動(dòng)生成,管理各個(gè) FormController 的工具類

dltwidget.DltWidgetImpl:大樂透卡片管理類,提供了創(chuàng)建卡片、更新卡片、刪除卡片、卡片點(diǎn)擊事件等行為的回調(diào)方法

fcsdwidget.FcsdWidgetImpl:福彩 3D 卡片管理類,提供了創(chuàng)建卡片、更新卡片、刪除卡片、卡片點(diǎn)擊事件等行為的回調(diào)方法

ssqwidget.SsqWidgetImpl:雙色球卡片管理類,提供了創(chuàng)建卡片、更新卡片、刪除卡片、卡片點(diǎn)擊事件等行為的回調(diào)方法

Constants:常量工具類。

MainAbility:HAP 的入口 ability,由 DevEco Studio 自動(dòng)生成。同時(shí)也是各個(gè)卡片對(duì)應(yīng)的 Ability,用來(lái)項(xiàng)各個(gè) FormController 分發(fā)事件。

default:

common,component/lottery:應(yīng)用首頁(yè)列表 item 組件;images :資源圖片

pages,home:應(yīng)用首頁(yè)

dlt_widget:

common:資源圖片存放目錄

pages/index,index.css :大樂透卡片 css 樣式;index.hml:大樂透卡片布局文件;index.json:包含頁(yè)面默認(rèn)值

fcsd_widget:目錄結(jié)構(gòu)同 dlt_widget。

ssq_widget:目錄結(jié)構(gòu)同 dlt_widget。

詳細(xì)實(shí)現(xiàn)過程

①創(chuàng)建雙色球卡片

在目錄 entry 上點(diǎn)擊右鍵,在彈出的菜單中選擇 New,然后在彈出的子菜單中點(diǎn)擊 Service Widget,如下圖所示:

在模板選擇界面,選擇基本的模板 Grid Pattern,點(diǎn)擊按鈕 Next,進(jìn)入到卡片配置界面:

首先配置卡片的名稱和描述;然后配置卡片關(guān)聯(lián)的 Page Ability;然后配置卡片的編程語(yǔ)言類型是 JS;接下來(lái)配置卡片的 JS 組件名稱;最后配置卡片支持的規(guī)格,勾選支持 2x4、4x4 規(guī)格。

重復(fù)上述步驟,創(chuàng)建出大樂透和雙色球卡片。運(yùn)行項(xiàng)目,長(zhǎng)按圖標(biāo)打開卡片管理界面,我們能看到剛創(chuàng)建的 3 類卡片,且每類卡片對(duì)應(yīng) 3 種不同樣式,如下圖所示:

②繪制雙色球卡片 UI

我們編寫雙色球界面:

《!--index.hml--》《div class=“container”》

《div》

《text class=“text”》雙色球《/text》

《text class=“text-small” style=“margin-left : 15px;” if=“{{ cardType2x4 || cardType4x4 }}”》每周二、四、日開獎(jiǎng)《/text》

《text class=“text” style=“margin-left : 75px;” if=“{{ cardType2x4 || cardType4x4 }}” onclick=“showMore”》 查看更多

《/text》

《/div》

《div style=“margin-top : 10px; align-content : center;”》

《text class=“text-small” style=“margin-right : 10px;”》第{{ lotteryData.lottery_no }}期《/text》

《text class=“text-small” if=“{{ cardType2x4 || cardType4x4 }}”》 開獎(jiǎng)日期:《/text》

《text class=“text-small”》 {{ lotteryData.lottery_date }}《/text》

《/div》

《div style=“flex-wrap : wrap; margin-top : 10px;”》

《text class=“ball” for=“{{ lotteryRed }}” tid=“id”》{{ $item }}《/text》

《text class=“ball” style=“background-color : blue;” for=“{{ lotteryBlue }}” tid=“id”》{{ $item }}《/text》

《/div》

《div class=“amount-box” if=“{{ cardType2x4 || cardType4x4 }}”》

《div style=“flex-direction : column;”》

《text class=“text-small”》本期全國(guó)銷量《/text》

《text class=“text-amount”》{{ lotteryData.lottery_sale_amount }}《/text》

《/div》

《text class=“diver”》《/text》

《div style=“flex-direction : column;”》

《text class=“text-small”》累計(jì)獎(jiǎng)池《/text》

《text class=“text-amount”》{{ lotteryData.lottery_pool_amount }}《/text》

《/div》

《/div》

《div style=“flex-direction : column; padding-top : 10px; margin-bottom: 20px;” if=“{{ cardType4x4 }}”》

《div style=“background-color : green;”》

《text class=“text-prize”》獎(jiǎng)項(xiàng)《/text》

《text class=“text-prize”》中獎(jiǎng)條件《/text》

《text class=“text-prize”》中獎(jiǎng)注數(shù)《/text》

《text class=“text-prize”》單注金額(元)《/text》

《/div》

《div for=“{{ lottery_prize }}”》

《text class=“text-prize”》{{ $item.prize_name }}《/text》

《text class=“text-prize”》{{ $item.prize_require }}《/text》

《text class=“text-prize”》{{ $item.prize_num }}《/text》

《text class=“text-prize”》{{ $item.prize_amount }}《/text》

《/div》

《/div》

《div》

《text class=“text-small” if=“{{ cardType2x4 || cardType4x4 }}”》更新時(shí)間:《/text》

《text class=“text-small”》{{ updateTime }}《/text》

《image class=“refresh” src=“/common/image_1.png” onclick=“updateData”》《/image》

《/div》《/div》

《!--index.json--》

{

“data”: {

“cardType2x2”: true,

“cardType2x4”: false,

“cardType4x4”: false,

“l(fā)otteryData”: {

“l(fā)ottery_no”: “21081”,

“l(fā)ottery_date”: “2021-07-20”,

“l(fā)ottery_sale_amount”:“344,437,194”,

“l(fā)ottery_pool_amount”:“997,378,346”

},

“l(fā)otteryRed”:[“01”,“03”,“05”,“18”,“22”,“23”],

“l(fā)otteryBlue”:[“01”],

“updateTime”: “2021/07/07 1459”,

“l(fā)ottery_prize”:[

{

“prize_name”:“一等獎(jiǎng)”,

“prize_num”:“4”,

“prize_amount”:“10,000,000”,

“prize_require”:“6+1”

},

{

“prize_name”:“二等獎(jiǎng)”,

“prize_num”:“135”,

“prize_amount”:“207,725”,

“prize_require”:“6+0”

},

{

“prize_name”:“三等獎(jiǎng)”,

“prize_num”:“879”,

“prize_amount”:“3,000”,

“prize_require”:“5+1”

},

{

“prize_name”:“四等獎(jiǎng)”,

“prize_num”:“45659”,

“prize_amount”:“200”,

“prize_require”:“5+0,4+1”

},

{

“prize_name”:“五等獎(jiǎng)”,

“prize_num”:“1001881”,

“prize_amount”:“10”,

“prize_require”:“4+0,3+1”

},

{

“prize_name”:“六等獎(jiǎng)”,

“prize_num”:“6962930”,

“prize_amount”:“5”,

“prize_require”:“2+1,1+1,0+1”

}

},

“actions”: {

}

}

《!--index.css--》

.container {

width: 100%;

height: 100%;

padding: 10px;

flex-direction: column;

}

.text {

font-size: 15px;

}

.text-small {

font-size: 11px;

}

.text-amount {

font-size: 14px;

font-weight: bold;

color: darkred;

}

.text-prize {

font-size: 11px;

flex-weight: 25;

min-height: 20px;

text-align: center;

}

.ball {

width: 20px;

height: 20px;

text-align: center;

font-size: 12px;

margin: 4px;

color: #FFFFFF;

border-radius: 20px;

background-color: red;

}

.amount-box {

flex-direction: row;

align-items: center;

height: 30px;

}

.diver {

width: 1px;

height: 20px;

background-color: red;

margin-left: 15px;

margin-right: 15px;

}

.refresh {

width: 22px;

height: 22px;

}

編寫完成后,重新運(yùn)行,不出意外你應(yīng)該能看到如下效果:

這里提下卡片 JS-UI 框架的坑:

框架提供了原子布局來(lái)控制元素在不同尺寸布局上的隱藏和展示,但怎么說(shuō)呢 ,一句話概括就是:你以為的并不是你以為的。

可以看到我這里放棄了 display-index 的使用,而采用通過 JAVA 端卡片的類型,來(lái)適配不同的 UI。

不同于應(yīng)用開發(fā)中的 JS UI 框架,這里的條件渲染不支持表達(dá)式。

css 不支持標(biāo)簽選擇器。

部分 css 樣式不能被繼承,例如給父 div 元素設(shè)置了 font-size,你會(huì)發(fā)現(xiàn) div 中的 text 組件并沒繼承上述樣式。

列表渲染的 for 循環(huán)的數(shù)組必須是 index.json data 對(duì)象的最外層。

不支持雙層 for 循環(huán)。

api6 不兼容 api5 的設(shè)備。

你會(huì)發(fā)現(xiàn)卡片 JSON 文件中的 data 對(duì)象數(shù)據(jù)結(jié)構(gòu)同接口返回的數(shù)據(jù)結(jié)構(gòu)有些差異,就是因?yàn)樯鲜鲈驅(qū)е碌摹?/p>

③獲取網(wǎng)絡(luò)數(shù)據(jù)

簡(jiǎn)單的 MVP 架構(gòu),采用 Retrofit+RxJava 作為異步網(wǎng)絡(luò)請(qǐng)求框架。

build.gradle 中添加 RxJava 和 Retrofit 的依賴:

// RxJava

api ‘io.reactivex.rxjava33.0.3’

implementation ‘io.openharmony.tpc.thirdlib1.0.0’// retrofit

implementation ‘com.squareup.retrofit22.7.0’

implementation “com.squareup.retrofit22.1.0”

implementation ‘com.squareup.retrofit22.9.0’

config.json 文件中添加網(wǎng)絡(luò)權(quán)限:

// config.json“deviceConfig”: {

“default”: {

“network”: {

“cleartextTraffic”: true,

“securityConfig”: {

“domainSettings”: {

“cleartextPermitted”: true,

“domains”: [

{

subdomains”: true,

“name”: “apis.juhe.cn”

},

{

“subdomains”: true,

“name”: “v.juhe.cn”

}

}

}

}

}

},

“module”: {

。..

“reqPermissions”: [

{

“name”: “ohos.permission.GET_NETWORK_INFO”

},

{

“name”: “ohos.permission.SET_NETWORK_INFO”

},

{

“name”: “ohos.permission.INTERNET”

}

],

。..

}

創(chuàng)建接口請(qǐng)求:

// LotteryAPI.javapublic interface LotteryAPI {

@POST(“/lottery/query”)

@FormUrlEncoded

Observable《LotteryBean》 queryDetail(@Field(“l(fā)ottery_id”) String lottery_id, @Field(“l(fā)ottery_no”) String lottery_no, @Field(“key”) String key);

}

P 層和 V 層的接口比較簡(jiǎn)單,不在羅列。編寫 P 層業(yè)務(wù)邏輯:

//MainPresenter.java@Overridepublic void loadLotteryData(long formId, String lotteryId) {

CachedLotteryDetailUtil.getLotteryDetail(lotteryId)

.flatMap((Function《LotteryBean, ObservableSource《ZSONObject》》) res -》 {

if (0 != res.getError_code()) {

Throwable throwable = new Throwable(res.getReason());

return Observable.error(throwable);

}

ZSONObject zsonObject = buildDataByResult(res);

return Observable.just(zsonObject);

})

.subscribeOn(Schedulers.io())

.observeOn(OpenHarmonySchedulers.mainThread())

.subscribe(new Observer《ZSONObject》() {

@Override

public void onSubscribe(@NonNull Disposable d) {

}

@Override

public void onNext(@NonNull ZSONObject result) {

if (mView != null) {

mView.onLoadDataSuccess(formId, result);

}

}

@Override

public void onError(@NonNull Throwable e) {

if (mView != null) {

mView.onLoadDataFailed(e);

}

}

@Override

public void onComplete() {

}

});

}

/**

* 根據(jù)接口返回的數(shù)據(jù),組裝成JS卡片需要的數(shù)據(jù)

*

* @param result

* @return

*/private ZSONObject buildDataByResult(LotteryBean result) {

LotteryBean.DetailBean detailBean = result.getResult();

ZSONObject data = new ZSONObject();

ZSONObject lotteryData = new ZSONObject();

lotteryData.put(“l(fā)ottery_no”, detailBean.getLottery_no());

lotteryData.put(“l(fā)ottery_date”, detailBean.getLottery_date());

lotteryData.put(“l(fā)ottery_sale_amount”, detailBean.getLottery_sale_amount());

lotteryData.put(“l(fā)ottery_pool_amount”, detailBean.getLottery_pool_amount());

data.put(“l(fā)otteryData”, lotteryData);

String[] ballArray = detailBean.getLottery_res().split(“,”);

if (“ssq”.equalsIgnoreCase(detailBean.getLottery_id())){

data.put(“l(fā)otteryRed”, Arrays.copyOfRange(ballArray, 0, 6));

data.put(“l(fā)otteryBlue”, Arrays.copyOfRange(ballArray, 6, 7));

} else if (“dlt”.equalsIgnoreCase(detailBean.getLottery_id())){

data.put(“l(fā)otteryRed”, Arrays.copyOfRange(ballArray, 0, 5));

data.put(“l(fā)otteryBlue”, Arrays.copyOfRange(ballArray, 5, 7));

} else if (“fcsd”.equalsIgnoreCase(detailBean.getLottery_id())){

data.put(“l(fā)otteryRed”, ballArray);

}

data.put(“updateTime”, new SimpleDateFormat(“yyyy-MM-dd HHss”).format(new Date()));

ZSONArray zsonArray = new ZSONArray();

for (LotteryBean.PrizeBean prizeBean : detailBean.getLottery_prize()) {

ZSONObject prize = new ZSONObject();

prize.put(“prize_name”, prizeBean.getPrize_name());

prize.put(“prize_num”, prizeBean.getPrize_num());

prize.put(“prize_amount”, prizeBean.getPrize_amount());

prize.put(“prize_require”, prizeBean.getPrize_require());

zsonArray.add(prize);

}

data.put(“l(fā)ottery_prize”, zsonArray);

return data;

}

④V 層調(diào)用 P 層,更新卡片

這里提下 MainAbility 的 onCreateForm 方法,它在進(jìn)入卡片管理界面時(shí)被調(diào)用,內(nèi)部通過 FormControllerManager 獲取了卡片對(duì)應(yīng)的 FormController,并調(diào)用了其 bindFormData() 方法。

由于我們卡片展示的數(shù)據(jù)來(lái)源于網(wǎng)絡(luò)請(qǐng)求,在對(duì)應(yīng)的 FormController 實(shí)現(xiàn)類中不太好獲得 formId,所以我們稍微改造一下 FormController 的 bindFormData 方法,把 formId 給傳進(jìn)去。

public abstract ProviderFormInfo bindFormData(long formId);

//MainAbility.javapublic class MainAbility extends AceAbility implements IMainContract.View {

private IMainContract.Presenter mPresenter;

@Override

public void onStart(Intent intent) {

。..

}

@Override

public void onStop() {

super.onStop();

}

@Override

protected ProviderFormInfo onCreateForm(Intent intent) {

HiLog.info(TAG, “onCreateForm”);

long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);

String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);

int dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2);

HiLog.info(TAG, “onCreateForm: formId=” + formId + “,formName=” + formName);

FormControllerManager formControllerManager = FormControllerManager.getInstance(this);

FormController formController = formControllerManager.getController(formId);

formController = (formController == null) ? formControllerManager.createFormController(formId,

formName, dimension) : formController;

if (formController == null) {

HiLog.error(TAG, “Get null controller. formId: ” + formId + “, formName: ” + formName);

return null;

}

return formController.bindFormData(formId);

}

@Override

protected void onUpdateForm(long formId) {

。..

}

@Override

protected void onDeleteForm(long formId) {

。..

}

@Override

protected void onTriggerFormEvent(long formId, String message) {

。..

}

@Override

public void onNewIntent(Intent intent) {

。..

}

private boolean intentFromWidget(Intent intent) {

。..

}

private String getRoutePageSlice(Intent intent) {

。..

}

private IMainContract.Presenter getPresenter() {

if (mPresenter == null) {

mPresenter = new MainPresenter(this);

}

return mPresenter;

}

/**

* 查詢彩票開獎(jiǎng)詳情

*

* @param formId

* @param lotteryId

*/

public void loadData(long formId, String lotteryId) {

getPresenter().loadLotteryData(formId, lotteryId);

}

@Override

public void onLoadDataSuccess(long formId, ZSONObject data) {

try {

// 更新卡片

updateForm(formId, new FormBindingData(data));

} catch (FormException e) {

e.printStackTrace();

}

}

@Override

public void onLoadDataFailed(Throwable exception) {

。..

}

}

//SsqWidgetImpl.javapublic class SsqWidgetImpl extends FormController{

private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, SsqWidgetImpl.class.getName());

public SsqWidgetImpl(Context context, String formName, Integer dimension) {

super(context, formName, dimension);

}

@Override

public ProviderFormInfo bindFormData(long formId) {

HiLog.info(TAG, “===== ssq bind form data”);

ZSONObject zsonObject = new ZSONObject();

ProviderFormInfo providerFormInfo = new ProviderFormInfo();

boolean is2x2 = dimension == DEFAULT_DIMENSION_2X2;

boolean is2x4 = dimension == DIMENSION_2X4;

boolean is4x4 = dimension == DIMENSION_4X4;

zsonObject.put(“cardType2x2”, is2x2);

zsonObject.put(“cardType2x4”, is2x4);

zsonObject.put(“cardType4x4”, is4x4);

providerFormInfo.setJsBindingData(new FormBindingData(zsonObject));

// 調(diào)用接口更新卡片數(shù)據(jù)

((MainAbility)context).loadData(formId,Constants.LOTTERY_ID_SSQ);

return providerFormInfo;

}

@Override

public void updateFormData(long formId, Object.。. vars) {

}

@Override

public void onTriggerFormEvent(long formId, String message) {

}

@Override

public Class《? extends AbilitySlice》 getRoutePageSlice(Intent intent) {

HiLog.info(TAG, “get the default page to route when you click card.”);

return null;

}

}

⑤簡(jiǎn)單的網(wǎng)絡(luò)優(yōu)化

由于 onCreateForm(Intent intent)方法會(huì)被調(diào)用多次,而每個(gè)類型的卡片請(qǐng)求的數(shù)據(jù)一樣,聚合 api 一天 100 次免費(fèi)請(qǐng)求的次數(shù)一會(huì)兒就用完了,故對(duì)此進(jìn)行一個(gè)簡(jiǎn)單的優(yōu)化。

優(yōu)先看內(nèi)存緩存中是否有,有且為過期(緩存默認(rèn) 10 分鐘有效期)則直接返回,否則阻塞等待接口請(qǐng)求完成,對(duì)于并發(fā)請(qǐng)求,若請(qǐng)求隊(duì)列已有相同的請(qǐng)求,則阻塞,否則創(chuàng)建新的請(qǐng)求。

若不關(guān)心接口調(diào)用的可略過本節(jié):

// CachedLotteryDetailUtil.java/**

* CachedLotteryDetailUtil

* 緩存彩票詳情工具類,防止在卡片創(chuàng)建時(shí),重復(fù)調(diào)用接口

*

* @author:xwg

* @since 2021-07-30

*/public class CachedLotteryDetailUtil {

//緩存的有效時(shí)間 默認(rèn)10分鐘

private static final long CACHE_TIME = 10 * 60 * 1000;

// 緩存的彩票結(jié)果

private static volatile ConcurrentHashMap《String, LotteryBean》 cacheResMap = new ConcurrentHashMap《》();

//緩存的請(qǐng)求時(shí)間

private static volatile ConcurrentHashMap《String, Long》 reqTimeMap = new ConcurrentHashMap《》();

//正在請(qǐng)求 對(duì)應(yīng)的lotteryId

private static volatile List《String》 requestingLotteryId;

/**

* 獲取彩票詳情

*

* @param lotteryId

* @return

*/

public static Observable《LotteryBean》 getLotteryDetail(String lotteryId) {

if (cacheResMap.get(lotteryId) != null) {

if (!isExpired(lotteryId)) {

return Observable.create(emitter -》 {

if (!emitter.isDisposed()) {

emitter.onNext(cacheResMap.get(lotteryId));

}

});

} else {

cacheResMap.remove(lotteryId);

}

}

if (isRequesting(lotteryId)) {

return waitToRequestEnd(lotteryId);

} else {

return requestDetail(lotteryId);

}

}

/**

* 是否正在請(qǐng)求

*

* @param lotteryId

* @return

*/

private static boolean isRequesting(String lotteryId) {

if (requestingLotteryId == null || lotteryId.isEmpty()) {

return false;

}

return requestingLotteryId.contains(lotteryId);

}

/**

* 阻塞等待請(qǐng)求結(jié)果

*

* @param lotteryId

* @return

*/

private static Observable《LotteryBean》 waitToRequestEnd(final String lotteryId) {

return Observable.create(emitter -》 {

while (isRequesting(lotteryId)) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

}

}

if (!emitter.isDisposed()) {

try {

if (cacheResMap != null && cacheResMap.get(lotteryId) != null && !isExpired(lotteryId)) {

LotteryBean lotteryBean = cacheResMap.get(lotteryId);

emitter.onNext(lotteryBean);

if (!emitter.isDisposed()) {

emitter.onComplete();

}

} else {

Throwable throwable = new Throwable(“請(qǐng)求異常,請(qǐng)稍后再試”);

emitter.onError(throwable);

}

} catch (Exception e) {

emitter.onError(e);

}

}

});

}

/**

* 聯(lián)網(wǎng)請(qǐng)求彩票詳情

*

* @param lotteryId

* @return

*/

private static Observable《LotteryBean》 requestDetail(final String lotteryId) {

if (requestingLotteryId == null) {

requestingLotteryId = new LinkedList《》();

}

if (!lotteryId.isEmpty() && !requestingLotteryId.contains(lotteryId)) {

requestingLotteryId.add(lotteryId);

}

return Services.createAPI(LotteryAPI.class).queryDetail(lotteryId, “”, Constants.JH_KEY)

.doOnError(throwable -》 {

requestingLotteryId.remove(lotteryId);

reqTimeMap.remove(lotteryId);

})

.doAfterNext(lotteryBean -》 {

requestingLotteryId.remove(lotteryId);

cacheResMap.put(lotteryId, lotteryBean);

reqTimeMap.put(lotteryId, System.currentTimeMillis());

});

}

/**

* 緩存是否已經(jīng)過期

*

* @return

*/

private static boolean isExpired(String lotteryId) {

if (reqTimeMap == null || reqTimeMap.get(lotteryId) == null) {

return true;

}

return System.currentTimeMillis() - reqTimeMap.get(lotteryId) 》 CACHE_TIME;

}

}

⑥給卡片增加刷新事件和查看更多事件

給卡片的 index.json 文件添加 actions,定義 showMore 為 router 事件,觸發(fā)這個(gè)事件會(huì)跳轉(zhuǎn)到指定的 abilityName 對(duì)應(yīng)的 Ability。

updateData 為 message 事件,觸發(fā)該事件,會(huì)回調(diào) Ability 中的 onTriggerFormEvent(long formId, String message)方法。

// index.json

{

“data”: {

。..

},

“actions”: {

“showMore”: {

“action”: “router”,

“abilityName”: “com.xwg.lotteryquery.MainAbility”,

“params”: {

“message”: “fcsd”

}

},

“updateData”: {

“action”: “message”,

“params”: {

“key”: “fcsd”

}

}

}

}

卡片的布局文件中添加點(diǎn)擊事件:

《!--跳轉(zhuǎn)應(yīng)用首頁(yè)事件--》《text class=“text” style=“margin-left : 120px;” if=“{{ cardType2x4 || cardType4x4 }}” onclick=“showMore”》 查看更多

《/text》

《!--點(diǎn)擊刷新按鈕事件--》《image class=“refresh” src=“/common/refresh.png” onclick=“updateData”》《/image》

// SsqWidgetImpl.java@Overridepublic void onTriggerFormEvent(long formId, String message) {

HiLog.info(TAG, “======== ssq handle card click event.”);

((MainAbility)context).loadData(formId,Constants.LOTTERY_ID_SSQ);

}

添加定時(shí)刷新:打開 config.json,對(duì)于標(biāo)簽“scheduledUpdateTime”設(shè)定的時(shí)刻,當(dāng)?shù)竭_(dá)之后,MainAbility 中卡片的回調(diào)方法 onUpdateForm() 就會(huì)被自動(dòng)調(diào)用。

updateDuration 默認(rèn)為 1,下面配置表示:雙色球卡片允許定時(shí)刷新,從 10:30 開始,每隔半小時(shí)刷新一次。

// config.json“forms”: [

{

“jsComponentName”: “ssq_widget”,

“isDefault”: true,

“scheduledUpdateTime”: “10:30”,

“defaultDimension”: “2*2”,

“name”: “SsqWidget”,

“description”: “雙色球”,

“colorMode”: “auto”,

“type”: “JS”,

“supportDimensions”: [

“2*2”,

“2*4”,

“4*4”

],

“updateEnabled”: true,

“updateDuration”: 1

}

。..

FormController 直接調(diào)用 MainAbility 的獲取數(shù)據(jù)方法:

// SsqWidgetImpl.java@Overridepublic void updateFormData(long formId, Object.。. vars) {

HiLog.info(TAG, “======== ssq update form data timing, default 30 minutes”);

((MainAbility)context).loadData(formId,Constants.LOTTERY_ID_SSQ);

}

⑦大樂透和福彩 3D 卡片的實(shí)現(xiàn)

這兩個(gè)卡片的實(shí)現(xiàn)過程和雙色球卡片基本一致,主要是 UI 上有些區(qū)別。大樂透卡片 4X4 樣式中,由于中獎(jiǎng)信息列表較長(zhǎng),引入了 list 和 list-item 組件,讓其可在卡片內(nèi)上下滾動(dòng),具體實(shí)現(xiàn)此處不再贅述,有興趣可閱讀源碼。

⑧歷史開獎(jiǎng)列表的實(shí)現(xiàn)

這個(gè)界面也相對(duì)簡(jiǎn)單,使用了 swiper 組件,左右滑動(dòng)或點(diǎn)擊頭部標(biāo)簽欄,完成***、福彩 3D 標(biāo)簽頁(yè)的切換,每個(gè)標(biāo)簽頁(yè)展示對(duì)應(yīng)的近 50 期開獎(jiǎng)結(jié)果,使用 list 和 list-item 組件渲染。

由于各列表 item 展示 UI 相近,故將其抽成了組件放置于 */common/component/lottery* 目錄下。

lottery 子組件部分,lottery.js:通過 props 接收外部傳入的 lotteryData 數(shù)據(jù)。

// lottery.jsexport default {

props: {

lotteryData: {}

}

}

lottery.hml 組件布局代碼:

《!--lottery.hml--》《div class=“item”》

《div class=“item-header”》

《text style=“font-weight : bold;”》第{{ lotteryData.lottery_no }}期《/text》

《text class=“remarks”》開獎(jiǎng)日期:{{ lotteryData.lottery_date }}《/text》

《/div》

《div style=“margin-left : 10px;”》

《text for=“{{ lotteryData.ballList }}”

class=“ball”

style=“background-color : {{ $idx 》= lotteryData.redBallCount ? ‘#6666FF’ : ‘#FF0033’ }};”

》{{ $item }}《/text》

《/div》

《div class=“amount-box”》

《div style=“flex-direction : column;”》

《text class=“text-small”》本期全國(guó)銷量《/text》

《text class=“text-amount”》{{ lotteryData.lottery_sale_amount }}《/text》

《/div》

《text class=“diver”》《/text》

《div style=“flex-direction : column;”》

《text class=“text-small”》累計(jì)獎(jiǎng)池《/text》

《text class=“text-amount”》{{ lotteryData.lottery_pool_amount || ‘0’ }}《/text》

《/div》

《/div》《/div》

css 比較簡(jiǎn)單,這里不再給出。

父組件實(shí)現(xiàn):

《!--home.hml--》《element name=‘lottery’ src=‘。./。./common/component/lottery/lottery.hml’》《/element》《div class=“container”》

《div class=“title-container”》

《text class=“title {{ currentIdx == 0 ? tabSelected : tabUnSelected }}” onclick=“onTabClick(0)”》雙色球《/text》

《text class=“title {{ currentIdx == 1 ? tabSelected : tabUnSelected }}” onclick=“onTabClick(1)”》大樂透《/text》

《text class=“title {{ currentIdx == 2 ? tabSelected : tabUnSelected }}” onclick=“onTabClick(2)”》福彩3D《/text》

《/div》

《swiper class=“swiper” id=“swiper” index=“0” indicator=“false” loop=“true”

digital=“false” on:change=“onChange”》

《div class=“swiperContent”》

《list class=“”》

《list-item for=“{{ ssqResList }}” class=“l(fā)ottery-item”》

《lottery lottery-data = “{{$item}}”》《/lottery》

《/list-item》

《/list》

《/div》

《div class=“swiperContent”》

《list class=“”》

《list-item for=“{{ dltResList }}” class=“l(fā)ottery-item”》

《lottery lottery-data = “{{$item}}”》《/lottery》

《/list-item》

《/list》

《/div》

《div class=“swiperContent”》

《list class=“”》

《list-item for=“{{ fcsdResList }}” class=“l(fā)ottery-item”》

《lottery lottery-data = “{{$item}}”》《/lottery》

《/list-item》

《/list》

《/div》

《/swiper》《/div》

《!--home.js--》

import http from ‘@ohos.net.http’;

const JH_URL = ‘http://apis.juhe.cn’const JH_KEY = ‘4931b786dd99c28e7e9990fb75c39fad’export default {

data: {

currentIdx: 0,

tabSelected: ‘tabSelected’,

tabUnSelected: ‘tabUnSelected’,

ssqResList: null, // 雙色球近期開獎(jiǎng)結(jié)果列表

dltResList: null, // 大樂透近期開獎(jiǎng)結(jié)果列表

fcsdResList: null, // 3D球近期開獎(jiǎng)結(jié)果列表

},

onInit() {

this.querySsqHis();

this.queryDltHis();

this.queryFcsdHis();

},

// 查詢雙色球歷史開獎(jiǎng)

querySsqHis() {

console.info(‘xwg== querySsqHis ----------’);

let _t = this

let httpRequest = http.createHttp();

let url = JH_URL + ‘/history’ + ‘?lottery_id=ssq&page_size=50&page=&key=’ + JH_KEY

httpRequest.request(url, (err, data) =》 {

if (err == null) {

let lotteryResList = JSON.parse(data.result).result.lotteryResList

_t.ssqResList = lotteryResList.map(item =》 {

item.ballList = item.lottery_res.split(“,”)

item.redBallCount = 6

return item

})

console.info(‘xwg== ssqResList:’ + JSON.stringify(_t.ssqResList));

} else {

console.info(‘xwg== error:’ + JSON.stringify(err));

}

});

},

// 查詢大樂透歷史開獎(jiǎng)

queryDltHis() {

let _t = this

let httpRequest = http.createHttp();

let url = JH_URL + ‘/history’ + ‘?lottery_id=ssq&page_size=50&page=&key=’ + JH_KEY

httpRequest.request(url, (err, data) =》 {

if (err == null) {

let dltRes = JSON.parse(data.result).result.lotteryResList

_t.dltResList = dltRes.map(item =》 {

item.ballList = item.lottery_res.split(“,”)

item.redBallCount = 5

return item

})

} else {

console.info(‘xwg== error:’ + JSON.stringify(err));

}

});

},

// 查詢福彩3D歷史開獎(jiǎng)

queryFcsdHis() {

let _t = this

let httpRequest = http.createHttp();

let url = JH_URL + ‘/history’ + ‘?lottery_id=ssq&page_size=50&page=&key=’ + JH_KEY

httpRequest.request(url, (err, data) =》 {

if (err == null) {

let fcsdRes = JSON.parse(data.result).result.lotteryResList

_t.fcsdResList = fcsdRes.map(item =》 {

item.ballList = item.lottery_res.split(“,”)

item.redBallCount = 3

return item

})

} else {

console.info(‘xwg== error:’ + JSON.stringify(err));

}

});

},

// swiper滑動(dòng)監(jiān)聽

onChange(value) {

this.currentIdx = value.index

},

// tab click事件

onTabClick(idx) {

this.currentIdx = idx

this.$element(‘swiper’).swipeTo({

index: idx

});

},

computed: {}

}

尚未解決的問題:這里引入了 http 組件進(jìn)行網(wǎng)絡(luò)請(qǐng)求,但在請(qǐng)求聚合接口時(shí),失敗率很高,但嘗試請(qǐng)求別的網(wǎng)站的 api 時(shí)沒有此現(xiàn)象,目前尚不知原因。

總結(jié)

由于這也是我第一次使用 JS UI 框架進(jìn)行卡片開發(fā)的項(xiàng)目,水平有限,難免會(huì)對(duì)官方部分 API 理解不到位甚至理解有誤的地方,希望大家也多多指正,共同進(jìn)步。

這一路上雖然磕磕巴巴,也有很多吐槽,但我們從卡片 JS-UI API 5 到 API 6 功能上逐漸靠攏應(yīng)用 JS-UI 上也能看出來(lái)鴻蒙的努力,給它點(diǎn)時(shí)間,相信它功能上會(huì)變得更強(qiáng)大、完善;對(duì)于開發(fā)也會(huì)變得更快捷、簡(jiǎn)單。

最后附上項(xiàng)目地址:lottery-query,請(qǐng)自行下載資源,歡迎交流學(xué)習(xí)。

https://gitee.com/chinasoft6_ohos/lottery-query

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 鴻蒙系統(tǒng)
    +關(guān)注

    關(guān)注

    183

    文章

    2627

    瀏覽量

    65790
  • HarmonyOS
    +關(guān)注

    關(guān)注

    79

    文章

    1946

    瀏覽量

    29740

原文標(biāo)題:超實(shí)用!鴻蒙彩票查詢卡片

文章出處:【微信號(hào):gh_834c4b3d87fe,微信公眾號(hào):OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    基于鴻蒙Next模擬卡片數(shù)據(jù)數(shù)據(jù)更新

    、介紹 基于鴻蒙Next模擬卡片數(shù)據(jù)數(shù)據(jù)更新二、場(chǎng)景需求 電商平臺(tái)產(chǎn)品信息更新、 客戶關(guān)系管理(CRM)系統(tǒng)、 社交媒體用戶資料更新、 健康管理
    發(fā)表于 08-30 15:32

    AIGC系統(tǒng)中聊天小助手卡片小組件嵌套設(shè)計(jì)實(shí)踐

    1.項(xiàng)目描述 領(lǐng)航者卡片樣式較多,有些卡片比較近似;可以用嵌套方案,實(shí)現(xiàn)個(gè)卡片,多個(gè)子單元 可拔插組件式
    的頭像 發(fā)表于 06-12 11:20 ?694次閱讀
    AIGC<b class='flag-5'>系統(tǒng)</b>中聊天小助手<b class='flag-5'>卡片</b>小組件嵌套設(shè)計(jì)實(shí)踐

    鴻蒙開發(fā)接口Ability框架:【@ohos.application.formInfo (FormInfo)】

    FormInfo模塊提供對(duì)卡片的相關(guān)卡片信息和狀態(tài)進(jìn)行設(shè)置和查詢的能力。
    的頭像 發(fā)表于 05-08 09:49 ?396次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)接口Ability框架:【@ohos.application.formInfo (FormInfo)】

    鴻蒙OS開發(fā)案例:【Stage模型卡片

    本示例展示了Stage模型卡片提供方的創(chuàng)建與使用。
    的頭像 發(fā)表于 04-09 17:13 ?830次閱讀
    <b class='flag-5'>鴻蒙</b>OS開發(fā)案例:【Stage模型<b class='flag-5'>卡片</b>】

    鴻蒙OS實(shí)戰(zhàn)開發(fā):【多設(shè)備自適應(yīng)服務(wù)卡片

    服務(wù)卡片的布局和使用,其中卡片內(nèi)容顯示使用了次開發(fā),多端部署的能力實(shí)現(xiàn)多設(shè)備自適應(yīng)。 用到了卡片擴(kuò)展模塊接口,[@ohos.app.form.FormExtensionAbil
    的頭像 發(fā)表于 04-09 09:20 ?717次閱讀
    <b class='flag-5'>鴻蒙</b>OS實(shí)戰(zhàn)開發(fā):【多設(shè)備自適應(yīng)服務(wù)<b class='flag-5'>卡片</b>】

    鴻蒙OS開發(fā)實(shí)例:【手?jǐn)]服務(wù)卡片

    服務(wù)卡片指導(dǎo)文檔位于“**開發(fā)/應(yīng)用模型/Stage模型開發(fā)指導(dǎo)/Stage模型應(yīng)用組件**”路徑下,說(shuō)明其極其重要。本篇文章將分享實(shí)現(xiàn)服務(wù)卡片的過程和代碼
    的頭像 發(fā)表于 03-28 22:11 ?1020次閱讀
    <b class='flag-5'>鴻蒙</b>OS開發(fā)實(shí)例:【手?jǐn)]服務(wù)<b class='flag-5'>卡片</b>】

    何在stm32cubemx中精確查詢

    stm32cubemx中finder功能的確很方便,但我遇到個(gè)問題 現(xiàn)在片子上pin復(fù)用功能越來(lái)越多,越來(lái)越方便 我同時(shí)需要多個(gè)接口,如,1個(gè)can,2個(gè)spi,1
    發(fā)表于 03-12 06:11

    emui10怎么升級(jí)鴻蒙系統(tǒng)

    鴻蒙系統(tǒng)是華為開發(fā)的新代操作系統(tǒng),而EMUI 10則是基于Android的用戶界面。目前,華為尚未公開說(shuō)明如何在EMUI 10設(shè)備上升級(jí)到
    的頭像 發(fā)表于 02-02 11:31 ?1883次閱讀

    何在鴻蒙系統(tǒng)上安裝Google Play

    隨著鴻蒙(HarmonyOS)系統(tǒng)的逐漸普及和用戶基數(shù)的增加,些用戶希望能在鴻蒙系統(tǒng)上使用Google Play商店以獲取更多應(yīng)用。然而,
    的頭像 發(fā)表于 01-31 17:13 ?1.3w次閱讀

    【年度精選】2023年度top5榜單——鴻蒙開發(fā)經(jīng)驗(yàn)

    的應(yīng)用。通過學(xué)習(xí)本教程,你將了解開源鴻蒙系統(tǒng)在不同場(chǎng)景下的應(yīng)用和實(shí)踐,激發(fā)你對(duì)人工智能和開源鴻蒙系統(tǒng)的興趣和創(chuàng)造力。 2、開發(fā)
    發(fā)表于 01-10 17:19

    張服務(wù)卡片,三個(gè)設(shè)計(jì)原則,讓HarmonyOS元服務(wù)卡片獲得更多用戶觸點(diǎn)

    元服務(wù)是種基于HarmonyOS API的全新服務(wù)提供方式,對(duì)于開發(fā)者來(lái)說(shuō)僅需要開發(fā)次,即可支持多終端設(shè)備運(yùn)行,以鴻蒙萬(wàn)能卡片等多種呈現(xiàn)形態(tài),向用戶提供更輕量化的服務(wù)。更輕便的開發(fā)
    的頭像 發(fā)表于 11-17 15:43 ?415次閱讀

    鴻蒙原生應(yīng)用開發(fā)-折疊屏、平板設(shè)備服務(wù)卡片適配

    、多設(shè)備卡片適配原則 為不同尺寸的卡片提供不同的功能 在卡片開發(fā)過程中請(qǐng)考慮適配不同尺寸的設(shè)備,特別是在折疊屏和平板設(shè)備上,設(shè)備屏幕尺寸的變化直接影響了
    發(fā)表于 11-16 10:10

    爆款元服務(wù)!教你如何設(shè)計(jì)高使用率卡片

    使用率和更多的用戶觸點(diǎn)呢?設(shè)計(jì)張好的卡片是你的不二之選。 那么如何設(shè)計(jì)張好的卡片呢? 在搞清楚這個(gè)問題之前,我們先來(lái)了解
    發(fā)表于 11-15 11:15

    華為鴻蒙系統(tǒng)

    華為鴻蒙系統(tǒng)(HUAWEI Harmony OS),是華為公司在2019年8月9日于東莞舉行的華為開發(fā)者大會(huì)(HDC.2019)上正式發(fā)布的操作系統(tǒng)。 華為鴻蒙
    發(fā)表于 11-02 19:39

    請(qǐng)問卡片電腦性能最強(qiáng)的是哪一個(gè)?

    卡片電腦性能最強(qiáng)的是哪一個(gè)?
    發(fā)表于 09-28 06:21