0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何在鸿蒙系统弄一个彩票查询卡片

OpenHarmony技术社区 来源:HarmonyOS技术社区 作者:中软国际 2021-09-06 09:17 次阅读

接触鸿蒙开发已经有 3 个来月了,最近开始在看鸿蒙卡片开发。因为之前的开发大都是基于 Java UI,但按官方的说法,JS 卡片相比 Java 卡片有更大的优势,故决定写个 JS 卡片的 demo 来练练手。

碰巧,前几天和媳妇儿在散步时捡到 1 元钱,没能交给警察叔叔,媳妇儿就提议“我们把它昧了吧,买张彩票。”

由于不是老 CM,没有关注开奖的习惯,想着要是能把开奖结果放在手机桌面显示就好了,这样就不会错过我一夜暴富的机会了。有了需求就开撸,然后就有了这篇文章!

项目简介

①本项目基于 API 6 开发,demo 运行在 API 6 以下的手机上会出现部分功能不能使用的现象。

②卡片功能上实现了双色球、大乐透、福彩 3D 的最近一期开奖结果查询,点击卡片“刷新”按钮,调用接口更新最新数据;点击“查看更多”按钮,跳转至应用主界面。

③应用主界面上实现了双色球、大乐透、福彩 3D 近 50 期开奖结果查看。

④以上数据均使用了聚合数据的 https://www.juhe.cn/docs/api/id/300 免费接口。

需申请 key,一个 key 一天可免费调用 100 次,如遇 key使 用次数过多导致接口请求失败情况时,开发者可自行申请 key 并替换 Constants.java 文件下的 JH_KEY 常量值),数据可能会有延迟。

⑤卡片开发部分使用了卡片的 JS UI 框架,但由于系统 PA 与 FA 相互调用的限制问题,卡片的业务逻辑部分仍然采用 Java 代码编写。

PS:最开始的想法是尽量可能的少依赖 Java 代码,故尝试了 JS FA 与 Java PA 相互调用的方式,但当应用进程被干掉时,Java 端无法再调用到 JS 端方法。

这样就导致 JS 只能写 UI 部分,业务逻辑还得 Java 层实现。有知道解决办法的也麻烦告知一声。

⑥卡片业务层使用 Java 开发,采用了简单的 MVP 架构,网络请求和数据处理部分使用了 rxjava3+retrofit 框架。

⑦应用主界面使用了 JS-UI 框架实现。

实现效果

请忽略我粗陋的 UI 设计和 GIF 的渣渣像素。

项目代码结构分析

base:

IBasePresenter:MVP 架构中 presenter 基类接口

IBaseView:MVP 架构中 view 基类接口

network:

bean,LotteryBean:彩票详情接口返回对应 model

CachedLotteryDetailUtil:彩票详情接口请求工具类,主要作用是防止重复调用详情接口

LogInterceptor:OKhttp 日志拦截工具类

LotteryAPI:接口请求类,通过 retrofit 注解,将接口返回数据转化为实体类

Services:配置 Retrofit 并提供

presenter:

IMainContract:MVP 中 view 与 presenter 的桥梁

MainPresenter:提供彩票详情接口的请求,并处理接口返回数据为卡片需要的 ZSONObject 对象

utils:

LogUtil:日志打印工具类

widget:

controller:FormController:创建卡片时自动生成,卡片管理器的抽象基类;FormControllerManager:创建卡片时自动生成,管理各个 FormController 的工具类

dltwidget.DltWidgetImpl:大乐透卡片管理类,提供了创建卡片、更新卡片、删除卡片、卡片点击事件等行为的回调方法

fcsdwidget.FcsdWidgetImpl:福彩 3D 卡片管理类,提供了创建卡片、更新卡片、删除卡片、卡片点击事件等行为的回调方法

ssqwidget.SsqWidgetImpl:双色球卡片管理类,提供了创建卡片、更新卡片、删除卡片、卡片点击事件等行为的回调方法

Constants:常量工具类。

MainAbility:HAP 的入口 ability,由 DevEco Studio 自动生成。同时也是各个卡片对应的 Ability,用来项各个 FormController 分发事件。

default:

common,component/lottery:应用首页列表 item 组件;images :资源图片

pages,home:应用首页

dlt_widget:

common:资源图片存放目录

pages/index,index.css :大乐透卡片 css 样式;index.hml:大乐透卡片布局文件;index.json:包含页面默认值

fcsd_widget:目录结构同 dlt_widget。

ssq_widget:目录结构同 dlt_widget。

详细实现过程

①创建双色球卡片

在目录 entry 上点击右键,在弹出的菜单中选择 New,然后在弹出的子菜单中点击 Service Widget,如下图所示:

在模板选择界面,选择基本的模板 Grid Pattern,点击按钮 Next,进入到卡片配置界面:

首先配置卡片的名称和描述;然后配置卡片关联的 Page Ability;然后配置卡片的编程语言类型是 JS;接下来配置卡片的 JS 组件名称;最后配置卡片支持的规格,勾选支持 2x4、4x4 规格。

重复上述步骤,创建出大乐透和双色球卡片。运行项目,长按图标打开卡片管理界面,我们能看到刚创建的 3 类卡片,且每类卡片对应 3 种不同样式,如下图所示:

②绘制双色球卡片 UI

我们编写双色球界面:

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

《div》

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

《text class=“text-small” style=“margin-left : 15px;” if=“{{ cardType2x4 || cardType4x4 }}”》每周二、四、日开奖《/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 }}”》 开奖日期:《/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”》本期全国销量《/text》

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

《/div》

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

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

《text class=“text-small”》累计奖池《/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”》奖项《/text》

《text class=“text-prize”》中奖条件《/text》

《text class=“text-prize”》中奖注数《/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 }}”》更新时间:《/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,

“lotteryData”: {

“lottery_no”: “21081”,

“lottery_date”: “2021-07-20”,

“lottery_sale_amount”:“344,437,194”,

“lottery_pool_amount”:“997,378,346”

},

“lotteryRed”:[“01”,“03”,“05”,“18”,“22”,“23”],

“lotteryBlue”:[“01”],

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

“lottery_prize”:[

{

“prize_name”:“一等奖”,

“prize_num”:“4”,

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

“prize_require”:“6+1”

},

{

“prize_name”:“二等奖”,

“prize_num”:“135”,

“prize_amount”:“207,725”,

“prize_require”:“6+0”

},

{

“prize_name”:“三等奖”,

“prize_num”:“879”,

“prize_amount”:“3,000”,

“prize_require”:“5+1”

},

{

“prize_name”:“四等奖”,

“prize_num”:“45659”,

“prize_amount”:“200”,

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

},

{

“prize_name”:“五等奖”,

“prize_num”:“1001881”,

“prize_amount”:“10”,

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

},

{

“prize_name”:“六等奖”,

“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;

}

编写完成后,重新运行,不出意外你应该能看到如下效果:

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

框架提供了原子布局来控制元素在不同尺寸布局上的隐藏和展示,但怎么说呢 ,一句话概括就是:你以为的并不是你以为的。

可以看到我这里放弃了 display-index 的使用,而采用通过 JAVA 端卡片的类型,来适配不同的 UI。

不同于应用开发中的 JS UI 框架,这里的条件渲染不支持表达式。

css 不支持标签选择器。

部分 css 样式不能被继承,例如给父 div 元素设置了 font-size,你会发现 div 中的 text 组件并没继承上述样式。

列表渲染的 for 循环的数组必须是 index.json data 对象的最外层。

不支持双层 for 循环。

api6 不兼容 api5 的设备。

你会发现卡片 JSON 文件中的 data 对象数据结构同接口返回的数据结构有些差异,就是因为上述原因导致的。

③获取网络数据

简单的 MVP 架构,采用 Retrofit+RxJava 作为异步网络请求框架。

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 文件中添加网络权限:

// 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”

}

],

。..

}

创建接口请求:

// LotteryAPI.javapublic interface LotteryAPI {

@POST(“/lottery/query”)

@FormUrlEncoded

Observable《LotteryBean》 queryDetail(@Field(“lottery_id”) String lottery_id, @Field(“lottery_no”) String lottery_no, @Field(“key”) String key);

}

P 层和 V 层的接口比较简单,不在罗列。编写 P 层业务逻辑:

//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() {

}

});

}

/**

* 根据接口返回的数据,组装成JS卡片需要的数据

*

* @param result

* @return

*/private ZSONObject buildDataByResult(LotteryBean result) {

LotteryBean.DetailBean detailBean = result.getResult();

ZSONObject data = new ZSONObject();

ZSONObject lotteryData = new ZSONObject();

lotteryData.put(“lottery_no”, detailBean.getLottery_no());

lotteryData.put(“lottery_date”, detailBean.getLottery_date());

lotteryData.put(“lottery_sale_amount”, detailBean.getLottery_sale_amount());

lotteryData.put(“lottery_pool_amount”, detailBean.getLottery_pool_amount());

data.put(“lotteryData”, lotteryData);

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

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

data.put(“lotteryRed”, Arrays.copyOfRange(ballArray, 0, 6));

data.put(“lotteryBlue”, Arrays.copyOfRange(ballArray, 6, 7));

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

data.put(“lotteryRed”, Arrays.copyOfRange(ballArray, 0, 5));

data.put(“lotteryBlue”, Arrays.copyOfRange(ballArray, 5, 7));

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

data.put(“lotteryRed”, 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(“lottery_prize”, zsonArray);

return data;

}

④V 层调用 P 层,更新卡片

这里提下 MainAbility 的 onCreateForm 方法,它在进入卡片管理界面时被调用,内部通过 FormControllerManager 获取了卡片对应的 FormController,并调用了其 bindFormData() 方法。

由于我们卡片展示的数据来源于网络请求,在对应的 FormController 实现类中不太好获得 formId,所以我们稍微改造一下 FormController 的 bindFormData 方法,把 formId 给传进去。

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;

}

/**

* 查询彩票开奖详情

*

* @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));

// 调用接口更新卡片数据

((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;

}

}

⑤简单的网络优化

由于 onCreateForm(Intent intent)方法会被调用多次,而每个类型的卡片请求的数据一样,聚合 api 一天 100 次免费请求的次数一会儿就用完了,故对此进行一个简单的优化。

优先看内存缓存中是否有,有且为过期(缓存默认 10 分钟有效期)则直接返回,否则阻塞等待接口请求完成,对于并发请求,若请求队列已有相同的请求,则阻塞,否则创建新的请求。

若不关心接口调用的可略过本节:

// CachedLotteryDetailUtil.java/**

* CachedLotteryDetailUtil

* 缓存彩票详情工具类,防止在卡片创建时,重复调用接口

*

* @author:xwg

* @since 2021-07-30

*/public class CachedLotteryDetailUtil {

//缓存的有效时间 默认10分钟

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

// 缓存的彩票结果

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

//缓存的请求时间

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

//正在请求 对应的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);

}

}

/**

* 是否正在请求

*

* @param lotteryId

* @return

*/

private static boolean isRequesting(String lotteryId) {

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

return false;

}

return requestingLotteryId.contains(lotteryId);

}

/**

* 阻塞等待请求结果

*

* @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(“请求异常,请稍后再试”);

emitter.onError(throwable);

}

} catch (Exception e) {

emitter.onError(e);

}

}

});

}

/**

* 联网请求彩票详情

*

* @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());

});

}

/**

* 缓存是否已经过期

*

* @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 事件,触发这个事件会跳转到指定的 abilityName 对应的 Ability。

updateData 为 message 事件,触发该事件,会回调 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”

}

}

}

}

卡片的布局文件中添加点击事件:

《!--跳转应用首页事件--》《text class=“text” style=“margin-left : 120px;” if=“{{ cardType2x4 || cardType4x4 }}” onclick=“showMore”》 查看更多

《/text》

《!--点击刷新按钮事件--》《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);

}

添加定时刷新:打开 config.json,对于标签“scheduledUpdateTime”设定的时刻,当到达之后,MainAbility 中卡片的回调方法 onUpdateForm() 就会被自动调用。

updateDuration 默认为 1,下面配置表示:双色球卡片允许定时刷新,从 10:30 开始,每隔半小时刷新一次。

// 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 直接调用 MainAbility 的获取数据方法:

// 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 卡片的实现

这两个卡片的实现过程和双色球卡片基本一致,主要是 UI 上有些区别。大乐透卡片 4X4 样式中,由于中奖信息列表较长,引入了 list 和 list-item 组件,让其可在卡片内上下滚动,具体实现此处不再赘述,有兴趣可阅读源码。

⑧历史开奖列表的实现

这个界面也相对简单,使用了 swiper 组件,左右滑动或点击头部标签栏,完成双色球、大乐透、福彩 3D 标签页的切换,每个标签页展示对应的近 50 期开奖结果,使用 list 和 list-item 组件渲染。

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

lottery 子组件部分,lottery.js:通过 props 接收外部传入的 lotteryData 数据。

// 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”》开奖日期:{{ 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”》本期全国销量《/text》

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

《/div》

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

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

《text class=“text-small”》累计奖池《/text》

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

《/div》

《/div》《/div》

css 比较简单,这里不再给出。

父组件实现:

《!--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=“lottery-item”》

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

《/list-item》

《/list》

《/div》

《div class=“swiperContent”》

《list class=“”》

《list-item for=“{{ dltResList }}” class=“lottery-item”》

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

《/list-item》

《/list》

《/div》

《div class=“swiperContent”》

《list class=“”》

《list-item for=“{{ fcsdResList }}” class=“lottery-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, // 双色球近期开奖结果列表

dltResList: null, // 大乐透近期开奖结果列表

fcsdResList: null, // 3D球近期开奖结果列表

},

onInit() {

this.querySsqHis();

this.queryDltHis();

this.queryFcsdHis();

},

// 查询双色球历史开奖

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));

}

});

},

// 查询大乐透历史开奖

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历史开奖

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滑动监听

onChange(value) {

this.currentIdx = value.index

},

// tab click事件

onTabClick(idx) {

this.currentIdx = idx

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

index: idx

});

},

computed: {}

}

尚未解决的问题:这里引入了 http 组件进行网络请求,但在请求聚合接口时,失败率很高,但尝试请求别的网站的 api 时没有此现象,目前尚不知原因。

总结

由于这也是我第一次使用 JS UI 框架进行卡片开发的项目,水平有限,难免会对官方部分 API 理解不到位甚至理解有误的地方,希望大家也多多指正,共同进步。

这一路上虽然磕磕巴巴,也有很多吐槽,但我们从卡片 JS-UI API 5 到 API 6 功能上逐渐靠拢应用 JS-UI 上也能看出来鸿蒙的努力,给它点时间,相信它功能上会变得更强大、完善;对于开发也会变得更快捷、简单。

最后附上项目地址:lottery-query,请自行下载资源,欢迎交流学习。

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

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 鸿蒙系统
    +关注

    关注

    183

    文章

    2602

    浏览量

    65249
  • HarmonyOS
    +关注

    关注

    79

    文章

    1768

    浏览量

    29231

原文标题:超实用!鸿蒙彩票查询卡片

文章出处:【微信号:gh_834c4b3d87fe,微信公众号:OpenHarmony技术社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    OpenHarmony开发案例:【电影卡片

    基于元服务卡片的能力,实现带有卡片的电影应用,介绍卡片的开发过程和生命周期实现。
    的头像 发表于 04-15 17:53 908次阅读
    OpenHarmony开发案例:【电影<b class='flag-5'>卡片</b>】

    鸿蒙系统三防平板怎么样

    、工地作业还是军事应用,这款平板电脑都能为用户提供稳定、可靠的性能支持。同时,其丰富的软件生态和便捷的操作体验也让用户能够更加方便地进行各种操作。如果你正在寻找款能够在恶劣环境下稳定运行的平板电脑,那么鸿蒙系统三防平板无疑是
    发表于 04-12 14:26

    鸿蒙OS实战开发:【多设备自适应服务卡片

    服务卡片的布局和使用,其中卡片内容显示使用了一次开发,多端部署的能力实现多设备自适应。 用到了卡片扩展模块接口,[@ohos.app.form.FormExtensionAbility
    的头像 发表于 04-09 09:20 462次阅读
    <b class='flag-5'>鸿蒙</b>OS实战开发:【多设备自适应服务<b class='flag-5'>卡片</b>】

    鸿蒙OS开发实例:【手撸服务卡片

    服务卡片指导文档位于“**开发/应用模型/Stage模型开发指导/Stage模型应用组件**”路径下,说明其极其重要。本篇文章将分享实现服务卡片的过程和代码
    的头像 发表于 03-28 22:11 694次阅读
    <b class='flag-5'>鸿蒙</b>OS开发实例:【手撸服务<b class='flag-5'>卡片</b>】

    何在stm32cubemx中精确查询

    stm32cubemx中finder功能的确很方便,但我遇到问题 现在片子上pin复用功能越来越多,越来越方便 我同时需要多个接口,如,1can,2spi,1
    发表于 03-12 06:11

    何在鸿蒙系统上安装Google Play

    随着鸿蒙(HarmonyOS)系统的逐渐普及和用户基数的增加,一些用户希望能在鸿蒙系统上使用Google Play商店以获取更多应用。然而,由于鸿蒙
    的头像 发表于 01-31 17:13 3300次阅读

    【年度精选】2023年度top5榜单——鸿蒙开发经验

    的应用。通过学习本教程,你将了解开源鸿蒙系统在不同场景下的应用和实践,激发你对人工智能和开源鸿蒙系统的兴趣和创造力。 2、开发
    发表于 01-10 17:19

    鸿蒙原生应用开发-折叠屏、平板设备服务卡片适配

    、多设备卡片适配原则 为不同尺寸的卡片提供不同的功能 在卡片开发过程中请考虑适配不同尺寸的设备,特别是在折叠屏和平板设备上,设备屏幕尺寸的变化直接影响了
    发表于 11-16 10:10

    爆款元服务!教你如何设计高使用率卡片

    使用率和更多的用户触点呢?设计张好的卡片是你的不二之选。 那么如何设计张好的卡片呢? 在搞清楚这个问题之前,我们先来了解
    发表于 11-15 11:15

    鸿蒙原生应用开发-元服务分发方式的调整及趋势

    屏,因此部分鸿蒙3.0的手机通过对角线滑动也打不开服务中心,那该如何进行开放式测试呢? 我们只需将服务中心卡片添加至桌面,然后点击卡片打开服务中心即可。 具体操作步骤:双指捏合桌面,
    发表于 11-14 16:02

    华为鸿蒙系统

    华为鸿蒙系统(HUAWEI Harmony OS),是华为公司在2019年8月9日于东莞举行的华为开发者大会(HDC.2019)上正式发布的操作系统。 华为鸿蒙
    发表于 11-02 19:39

    鸿蒙操作系统的前世今生

    鸿蒙操作系统就正式诞生了! 02、HarmonyOS 2.0和OpenHarmony 2.0的关系 在介绍HarmonyOS 2.0和OpenHarmony 2.0的关系之前,首先要介绍
    发表于 10-08 19:55

    请问卡片电脑性能最强的是哪一个

    卡片电脑性能最强的是哪一个
    发表于 09-28 06:21

    HarmonyOS元服务开发实践:桌面卡片字典

    不同卡片特征显示同一个字的不同内容,基于用户习惯可选择喜欢的卡片。 3.万能卡片刷新:用户点击卡片刷新按钮查看新内容,同时
    发表于 08-24 16:55

    HarmonyOS 3.1上实现计步卡片

    本篇帖子是参考 Codelab 基于 Stage 模型 JS 服务卡片,使用最新 ArkTS 元服务开发的,实现带有卡片的计步应用,用于介绍卡片的开发及生命周期实现。
    的头像 发表于 05-29 11:10 494次阅读