内容摘要:想了解更多内容,请访问:和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com前言本人是JS攻城狮,java属于初学状态,写的不好的地方还请多多指教!为什么决定开发一个

想了解更多内容,星座运势请访问:
和华为官方合作共建的卡卡片开鸿蒙技术社区
https://harmonyos.51cto.com
前言
本人是JS攻城狮,java属于初学状态,星座运势写的卡卡片开不好的地方还请多多指教!
为什么决定开发一个星座运势服务卡片?
主要有以下几点:
老年人和女性都比较关心运势,卡片类型刚好能够清晰展示运势,星座运势同时也不需要太多操作,卡卡片开与服务卡片理念契合(弱交互、星座运势强服务)。卡卡片开 聚合api上刚好有相应接口,星座运势可以直接调用,卡卡片开比较方便。星座运势
实现效果
实现了三个卡片(2X2、卡卡片开2X4、星座运势4X4)和一个内容页,卡卡片开基于api6开发(API 6目前只支持运行在Phone设备的星座运势模拟器上,其它设备暂不支持运行),如何使用API 6点击查看?
卡片使用原子化布局实现,根据卡片规格自适应。
内容页可查询关注的星座,调用接口来获取运势。

创建项目
搭建环境,下载开发工具并安装。下载和安装官方文档
打开安装好的DevEco Studio软件,创建项目。官方文档


填写创建的工程信息 官方文档
Project Type:工程的类型。
服务器租用 Service:原子化服务。原子化服务调试、运行时,在设备桌面上没有应用图标、免安装的。 Application:传统方式需要安装的应用。 Compatible API Version:兼容的SDK最低版本。 Show in Service Center:是否在服务中心露出。如果Project Type为Service,则会同步创建一个2*2的服务卡片模板。

创建好的模板,我们主要会用到以下目录:

卡片JSUI部分
hml部分
布局

代码
<div class="normal_container" > <!-- 2X2 2X4 布局--> <div class="container_top" @click="clickToApp" > <!-- 2X2 布局--> <div class="top_left"> <div class="left_item"> <text class="left_item_text conName"> { { conName }} </text> </div> <div class="left_item"> <text class="left_item_text conDate"> { { conDate }} </text> </div> <div class="conAll left_item"> <div class="s_score"> <text class="left_item_text label_2X2"> 综合 </text> <image class="s_star_2X2" for="{ { conAllStarY }}" src="/common/starY.png"></image> <image class="s_star" for="{ { conAllStarN }}" src="/common/starN.png"></image> </div> </div> <div class="conPari left_item"> <text class="left_item_text" > 速配星座 </text> <text class="left_item_text conPari_text"> { { conPari }} </text> </div> </div> <!-- 2X4 布局--> <div class="top_right"> <image src="{ { imgSrc }}" class="con_image" ></image> </div> </div> <!-- 4X4 布局半部分--> <div class="container_bottom" @click="clickToApp" > <div class="list_item item_line_top" > <div class="s_score"> <text class="label"> 爱情 </text> <image class="s_star" for="{ { conLoveStarY }}" src="/common/starY.png"></image> <image class="s_star" for="{ { conLoveStarN }}" src="/common/starN.png"></image> </div> <div class="s_score" style="margin-left: 40px;" > <text class="label"> 理财 </text> <image class="s_star" for="{ { conMoneyStarY }}" src="/common/starY.png"></image> <image class="s_star" for="{ { conMoneyStarN }}" src="/common/starN.png"></image> </div> </div> <div class="list_item"> <div class="s_score"> <text class="label"> 工作 </text> <image class="s_star" for="{ { conWorkStarY }}" src="/common/starY.png"></image> <image class="s_star" for="{ { conWorkStarN }}" src="/common/starN.png"></image> </div> <div class="s_score" style="margin-left: 40px;" > <text class="label"> 健康 </text> <image class="s_star" for="{ { conHealthStarY }}" src="/common/starY.png"></image> <image class="s_star" for="{ { conHealthStarN }}" src="/common/starN.png"></image> </div> </div> <div class="item_line_bottom" > <div class="s_score"> <text class="label"> 幸运数字 </text> <text class="score_text"> { { conNumber }} </text> </div> <div class="s_score" style="margin-left: 78px;" > <text class="label"> 幸运颜色 </text> <text class="score_text"> { { conColor }} </text> </div> </div> <div class="list_item_more" > <div class="list_item_box1"> <text class="item_title_text"> 综合概述 </text> </div> <div class="list_item_box2"> <text class="item_sub_text"> { { conSummary }} </text> </div> </div> </div> </div>
开发过程的问题
hml文本文字必须写在text标签中 <text>文字</text> hml 动态数据不可以一下 写两个 <text >// 只会渲染第一个 { { conName }} { { conData }} </text> <text > // 不支持,直接当文本处理 { { conName + conDate }} </text> hml文件中的{ { }},动态数据不支持表达式,比如 5 - 1; <div if="{ { flag - 1}}"> <div if="{ { !flag}}"> list list-item 标签中无法触发点击事件 <list @click="handle"> // 无法触发 <list-item> </list>
css部分
代码部分
.normal_container { width: 100%; height: 100%; display: flex; flex-wrap: wrap; background: linear-gradient(to right, #6a11cb 0%, #667eea 100%); } .container_top{ display: flex; } /*2X2 布局样式 */ .top_left{ width: 165px; height: 150px; display-index: 3; display: flex; flex-direction: column; padding: 12px 10px; } .left_item{ margin-top: 8px; } .left_item_text{ font-size: 16px; color: #ffffff; } .conDate{ font-size: 14px; } .conPari_text{ font-size: 18px; margin-left: 6px; } .s_star_2X2{ width: 16px; height: 16px; margin-left: 2px; } .label_2X2{ margin-right: 8px; font-size: 16px; color: #fff; } /*2X4 布局样式右 */ .top_right{ width: 150px; height: 150px; display-index: 2; display: flex; align-items: center; justify-content: center; } .con_image{ width: 120px; height: 120px; border-radius: 70px; border: 4px solid #fdfbfb; } /*4X4 布局样式下 */ .container_bottom{ width: 100%; display-index: 1; padding: 0px 10px; display: flex; flex-direction: column; } .list_item{ margin-bottom: 6px; } .list_item_more{ display: flex; flex-direction: column; } .list_item_box2{ width: 100%; height: 80px; margin-top: -5px; } .item_title_text{ font-size: 18px; color: #fff; padding: 8px 0 0px; } .item_sub_text{ font-size: 14px; color: #fff; /*配套使用*/ text-overflow: ellipsis; max-lines: 3; /*配套使用*/ line-height: 22px; } .item_line_bottom{ border-bottom: 1px solid #eee; padding-bottom: 8px; } .item_line_top{ border-top: 1px solid #eee; padding-top: 8px; } /* 通用样式 */ /* 评分星 */ .s_score{ display: flex; align-items: center; } .s_star{ width: 14px; height: 14px; margin-left: 2px; } .label{ margin-right: 8px; font-size: 14px; color: #fff; } .score_text{ font-size: 14px; color: #fff; margin-left: 6px; }
遇到的问题
css boder-raduis 不支持百分比 css 不支持 嵌套 .divClass .text{ } // 不支持 css font-size color 不支持继承,font-size color等文本必须写在text标签内,写在父级就不会相应 css 不支持大部分伪类选择器(:nth-child(n)、:before、:after)
json部分
{ "data": { "imgSrc": "/common/constellation/天蝎.jpg", "conName": "天蝎座", "conDate": "2021年07月27日", "conPari": "金牛座", "conNumber": 1, "conColor": "古铜色", "conSummary": "有些思考的小漩涡,可能让你忽然的放空,生活中许多的细节让你感触良多,
亿华云五味杂陈,常常有时候就慢动作定格,想法在某处冻结停留,陷入一阵自我对话的沉思之中,这个时候你不喜欢被打扰或询问,也不想让某些想法曝光,个性变得有些隐晦。", "conAllStarY": [], "conAllStarN": [], "conLoveStarY": [], "conLoveStarN": [], "conMoneyStarY": [], "conMoneyStarN": [], "conWorkStarY": [], "conWorkStarN": [], "conHealthStarY": [], "conHealthStarN": [] }, "actions": { "clickToApp": { // 跳转事件,跳转到相应内容页 "action": "router", "abilityName": "com.example.fortune.MainAbility", "params": { "message": ""// 点击传递数据 } } } }
遇到的问题
json中定义数组,如 “conAllStarY”: [“Y”, “Y”], 在java中动态修改它为[“Y”, “Y”, “Y”, “Y”],则实际渲染出来有问题。打印出来的数组正确,for循环出来的数组只有两位。最后将初始数组改为空数组才解决了这个bug。 <text>{ { conWorkStarY }}</text> <text for="{ { conWorkStarY}}" >a</text>
结果:
[“Y”, “Y”, “Y”, “Y”]aa
java代码逻辑
公共类
public class UpdataJSData { private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, UpdataJSData.class.getName()); // 数据请求 public static void getJsonData(String constellation, ZZRCallBack.CallBackString callBackName ) { String url = "http://web.juhe.cn:8080/constellation/getAll"; Map<String,String> map = new HashMap<>(); map.put("key", "9d075905a3e1c33224ac5bb42f4de458"); map.put("consName", constellation); map.put("type", "today"); ZZRHttp.get(url, map, callBackName); } // 将请求到的数据更新到fortuneData中,并返回回去 public static ZSONObject setJson(String response) { ZSONObject fortuneData = ZSONObject.stringToZSON(response); int error_code = fortuneData.getIntValue("error_code"); if (error_code == 0) { HiLog.info(TAG,"返回的数据正确,设置js中的json数据"); String[][] all = formatConversion(fortuneData.getString("all")); String[][] love = formatConversion(fortuneData.getString("love")); String[][] money = formatConversion(fortuneData.getString("money")); String[][] work = formatConversion(fortuneData.getString("work")); String[][] health = formatConversion(fortuneData.getString("health")); HiLog.info(TAG,health.toString()); fortuneData.put("imgSrc", "/common/constellation/" + fortuneData.getString("name").toString().substring(0, 2) + ".jpg"); fortuneData.put("conName", fortuneData.getString("name"));// 星座名称 fortuneData.put("imgSrc", "/common/constellation/" + fortuneData.getString("name").toString().substring(0, 2) + ".jpg");// 星座图片路径拼接 fortuneData.put("conName", fortuneData.getString("name"));// 星座名称 fortuneData.put("conDate", fortuneData.getString("datetime"));// 当前事件 fortuneData.put("conPari", fortuneData.getString("QFriend"));// 速配星座 fortuneData.put("conNumber", fortuneData.getString("number"));// 幸运数组 fortuneData.put("conColor", fortuneData.getString("color"));// 幸运颜色 fortuneData.put("conSummary", fortuneData.getString("summary"));// 描述信息 fortuneData.put("conAllStarY", all[0]);// 综合评分 标记星 fortuneData.put("conAllStarN", all[1]);// 综合评分 未标记星 fortuneData.put("conLoveStarY", love[0]);// 爱情评分 标记星 fortuneData.put("conLoveStarN", love[1]);// 爱情评分 未标记星 fortuneData.put("conMoneyStarY", money[0]);// 理财评分 标记星 fortuneData.put("conMoneyStarN", money[1]);// 理财评分 未标记星 fortuneData.put("conWorkStarY", work[0]);// 工作评分 标记星 fortuneData.put("conWorkStarN", work[1]);// 工作评分 未标记星 fortuneData.put("conHealthStarY", health[0]);// 健康评分 标记星 fortuneData.put("conHealthStarN", health[1]);// 健康评分 未标记星 } else { HiLog.info(TAG,"请求出错,code=" + error_code); } return fortuneData; } // 对分数数据进行封装,转换为二维数组 private static String[][] formatConversion(String star) { int myData = Integer.parseInt(star); int myDataY = myData / 20; int myDataN = 5 - myDataY; HiLog.info(TAG,"star=" + star + "myDataY=" + myDataY + " myDataN=" + myDataN); String myListY[] = new String[myDataY]; String myListN[] = new String[myDataN]; for(int i = 0; i < myDataY; i++) { myListY[i] = "Y"; } for(int i = 0; i < myDataN; i++) { myListN[i] = "N"; } String[][] myList = { myListY, myListN}; return myList; } }
数据初始化
创建卡片时,调用MainAbility中onCreateForm方法 实现代码 protected ProviderFormInfo onCreateForm(Intent intent) { HiLog.info(TAG, "更新数据:onCreateForm"); ProviderFormInfo providerFormInfo = new ProviderFormInfo(); 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; } // 调用公共类中获取数据,传入一个callback方法 UpdataJSData.getJsonData("处女座", new ZZRCallBack.CallBackString() { public void onFailure(int code, String errorMessage) { //http访问出错了,此部分内容在主线程中工作; //可以更新UI等操作,请不要执行阻塞操作。 HiLog.info(TAG,"更新数据:数据请求失败"+code+","+errorMessage); } public void onResponse(String response) { //http访问成功,此部分内容在主线程中工作; //可以更新UI等操作,但请不要执行阻塞操作。 HiLog.info(TAG,"更新数据:数据请求成功"); ZSONObject fortuneData = setJson(response); try { HiLog.info(TAG,"更新数据"); updateForm(formId, new FormBindingData(fortuneData)); } catch (FormException e) { HiLog.info(TAG,"更新数据失败"); } } }); return providerFormInfo; }
采用定时刷新,源码库星座运势不需要周期性刷新
config.js 中设置定时刷新的时间 "forms": [ { "scheduledUpdateTime": "10:12", // 定时刷新时间点 "updateEnabled": true, // 是否周期性刷新 "updateDuration": 0, ... } ] 定时刷新会触发widget->WidgetImpl文件的updateFormData方法 public void updateFormData(long formId, Object... vars) { HiLog.info(TAG, "更新数据"); UpdataJSData.getJsonData("双鱼座", new ZZRCallBack.CallBackString() { public void onFailure(int code, String errorMessage) { //http访问出错了,此部分内容在主线程中工作; //可以更新UI等操作,请不要执行阻塞操作。 HiLog.info(TAG,"数据请求失败"+code+","+errorMessage); } public void onResponse(String response) { //http访问成功,此部分内容在主线程中工作; //可以更新UI等操作,但请不要执行阻塞操作。 HiLog.info(TAG,"数据请求成功"); ZSONObject fortuneData = setJson(response); try { HiLog.info(TAG,"更新数据"); ((MainAbility)context).updateForm(formId, new FormBindingData(fortuneData)); //调用MainAbility的方法updateForm(),并将formBindingData作为第二个实参 } catch (FormException e) { HiLog.info(TAG,"更新数据失败"); } } }); }
内容页部分
内容页布局比4X4的卡片多了个输入框,可进行星座搜索 内容页布局和css同卡片比较相似,这边就贴一下js代码 import http from @ohos.net.http;// 导入http export default { data: { conName: "天蝎座", options: { key: "9d075905a3e1c33224ac5bb42f4de458", type: "today" }, url: "", conData: { name: "天蝎座", datetime: "2021年7月28号", img: "/common/images/constellation/天蝎.jpg", QFriend: "金牛", number: "4", color: "粉色", summary: "有些思考的小漩涡,可能让你忽然的放空,生活中许多的细节让你感触良多,五味杂陈,常常有时候就慢动作定格,想法在某处冻结停留,陷入一阵自我对话的沉思之中,这个时候你不喜欢被打扰或询问,也不想让某些想法曝光,个性变得有些隐晦。" }, responseData: { } }, onInit() { this.getHttp();// 发送请求 }, // 输入框改变触发 changeCon(v) { this.conName = v.value; console.log(v.value+ change conName); }, getHttp() { this.url = `http://web.juhe.cn:8080/constellation/getAll?key=${ this.options.key}&consName=${ this.conName}&type=${ this.options.type}` let httpRequest = http.createHttp(); httpRequest.request(this.url, { method: GET },(err, data) => { if (err == null) { let conData = JSON.parse(data.result); console.log(Result: + data.result); if (conData.error_code == 0) { conData.img = `/common/images/constellation/${ (conData.name).slice(0, 2)}.jpg` conData.allY = this.getNewArr(conData.all, "Y"); conData.allN = this.getNewArr(conData.all, "N"); conData.healthY = this.getNewArr(conData.health, "Y"); conData.healthN = this.getNewArr(conData.health, "N"); conData.loveY = this.getNewArr(conData.love, "Y"); conData.loveN = this.getNewArr(conData.love, "N"); conData.moneyY = this.getNewArr(conData.money, "Y"); conData.moneyN = this.getNewArr(conData.money, "N"); conData.workY = this.getNewArr(conData.work, "Y"); conData.workN = this.getNewArr(conData.work, "N"); this.conData = conData; } else { console.error("请求失败"); } } else { console.log(error: + JSON.stringify(err)); } }); }, // 数字转化为数组不然for循环不出来 getNewArr(score, type){ score = parseInt(parseInt(score) / 20); let arr = []; if (type === "N") { console.log(type + type) score = 5 - score; for (var i = 0; i < score; i++) { arr.push(type); } } else { for (var i = 0; i < score; i++) { arr.push(type); } } return arr; } } 需要注意hml中for循环时,最好绑定tid,不然会复用。 搜索框的value不是双向绑定的,这边需要监听个change事件,来改变value。 js部分数据请求时,采用api6,需要自己更改配置设置为兼容api6 官方文档
总结
JSUI部分,主要是一些布局和样式类,是无法进行数据处理的。做一些弱交互的卡片是非常棒的。 Java部分主要也就是用到了三个函数。onStart:主要做一些js方事件处理。onCreateForm:创建卡片的时候需要。updateFormData:定时刷新和周期性刷新会走到这里 卡片开发目前来说bug还是挺多,限制也挺多的,期待做的越来越好。
源码地址
想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com
