12306鸿蒙小卡片,出差必备神器!
在没有鸿蒙之前,我相信很多人座高铁是这样的:
买了高铁票,打车去高铁站
进入高铁站大门然后安检
在候车室等车和等检票
到了站台等高铁
上车
我怕乘错车和晚上车经常会在 3,4 步骤的时候打开 12306,然后打开车票,查看车票信息和发车时间,保证我是准点去检票座车,并且没有乘错车。
像我这种经常去出差的人,一到高铁站会一直重复的去打开 12306 去查看车票信息,我觉得是一件非常非常麻烦的事情。
还有现在的 12306 不会把车票信息以短信的方式发给我们,情景智能都不能用。那如何解决这个问题呢,答案就是鸿蒙小卡片。
12306 小卡片效果展示
可以看到车票的所有重要信息都展示了出来,这样在出发去高铁站之前,我们只需要长按选择这个 12306 小卡片,添加到桌面上,然后长按小卡片出现编辑页面。
在小卡片编辑页面添加车票相关信息,点击查询后会查询到相关列车信息,然后点就确认就会把数据放到小卡片上去了。
12306 小卡片开发准备
①创建工程
详见鸿蒙官网。
②工程中添加小卡片
详见鸿蒙官网。
③目录结构
④添加 12306 域名
"deviceConfig": {
"default": {
"network": {
"securityConfig": {
"domainSettings": {
"cleartextPermitted": true,
"domains": [
{
"name": "search.12306.cn"
},
{
"name": "kyfw.12306.cn"
}
]
}
}
}
}
},
小卡片页面开发
①index.hml
小卡片主要分成两大模块:车票基本信息和车次列表。
车票基本信息里面有:开始地址、开始时间、车次号、日期、座位号、结束地址和结束时间。
车次列表里面有:到达时间,发车时间,所需时间和正点状态。
<!-- 车次列表 -->
<div>
<list class="station_list">
<list-item class="station_list_item" clickeffect="false" for="{{stationList}}">
<text class="station_start_time">{{$item.start_time}}</text>
<div class="circle"></div>
<text class="station_name_text">{{$item.station_name}}</text>
<text class="station_mess_text grey_color">{{$item.arrive_time}}</text>
<text class="station_mess_text grey_color">{{$item.running_time}}</text>
<text class="station_mess_text grey_color">{{$item.arrive_day_str}}</text>
</list-item>
</list>
</div>
②index.css
.main_container{
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-color: #FFFFFF;
}
.title_container{
margin-top: 5px;
}
.title_time_text{
font-size: 10px;
width: 100%;
height: 10px;
text-align: center;
color: darkgray;
}
.title_address_text{
font-size: 16px;
width: 100%;
height: 20px;
text-align: center;
color: #5F80FF;
}
.title_div{
height: 30px;
display: flex;
flex-direction: column;
}
.title_ticket_text{
width: 300px;
font-size: 10px;
text-align: center;
color: #5F80FF;
}
.teain_d_text{
width: 120px;
height: 35px;
/ background-image: url("/common/jt.png");/
background-size: 100% 100%;
text-align: center;
font-size: 10px;
line-height: 15px;
}
.station_list{
width: 100%;
height: 120px;
margin-left: 10px;
margin-top: 5px;
}
.station_list_item{
}
.station_name_text{
width: 80px;
font-size: 12px;
padding-left: 5px;
border-left-style: solid;
border-left-color: #5F80FF;
border-left-width: 1px;
padding-bottom: 8px;
}
.grey_color{
color: rgb(80,80,80);
}
.station_mess_text{
width: 60px;
text-align: center;
font-size: 10px;
}
.station_start_time{
font-size: 11px;
}
.circle{
width: 8px;
height: 8px;
border-radius: 100px;
border: 4px solid #5F80FF;
margin-top: 3.5px;
left: 4.5px;
}
③index.json
小卡片通过 json 配置数据绑定和点击事件,车票的基本信息保存在 treainData 里面,车次列表保存在 stationList 里面。
{
"data": {
"treainData": {
"startAddress": "",
"startTime": "",
"endAddress": "",
"endTime": "",
"trainDataString": ""
},
"stationList": [],
},
"actions": {
"getNewData": {
"action": "message",
"params": {
"message": "getNewData"
}
}
}
}
小卡片编辑功能的开发准备
鸿蒙的小卡片可以创建很多个,最大好像是 8 个,每个小卡片肯定是展现不一样的信息,所以需要添加编辑页面来让用户编辑这个小卡片,没有编辑页面的小卡片是没有灵魂的。
首先需要新建一个 TrainConfigAbility,在里面 setInstanceName TrainConfig:
package com.example.phone.ability.train;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.ace.ability.AceAbility;
public class TrainConfigAbility extends AceAbility {
public static Long cardId;
@Override
public void onStart(Intent intent) {
setInstanceName("TrainConfig");
IntentParams params = intent.getParams();
cardId = (long) params.getParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY);
super.onStart(intent);
}
@Override
public void onStop() {
super.onStop();
}
}
然后在小卡片配置 JSON 里面添加:
"formConfigAbility": "ability://com.example.phone.ability.train.TrainConfigAbility"
我的小卡片配置文件时这样的:
{
"name": "com.example.phone.ability.train.TrainAbility",
"icon": "$media:icon",
"description": "$string:widget_trainability_description",
"formsEnabled": true,
"label": "$string:entry_TrainAbility",
"type": "page",
"forms": [
{
"jsComponentName": "train",
"isDefault": true,
"scheduledUpdateTime": "10:30",
"defaultDimension": "24",
"name": "Train",
"description": "train card",
"colorMode": "auto",
"type": "JS",
"supportDimensions": [
"24"
],
"updateEnabled": true,
"updateDuration": 1,
"formConfigAbility": "ability://com.example.phone.ability.train.TrainConfigAbility"
}
],
"launchType": "singleton"
},
小卡片数据库
关系型数据库加入包
在对应的 entry 的 build.gradle 中添加包:
dependencies {
implementation fileTree(dir: 'libs', include: ['.jar', '.har'])
testCompile 'junit:junit:4.12'
compile files(ORM_ANNOTATIONS_JAVA, ORM_ANNOTATIONS_PROCESSOR_JAVA, JAVAPOET_JAVA)
annotationProcessor files(ORM_ANNOTATIONS_JAVA, ORM_ANNOTATIONS_PROCESSOR_JAVA, JAVAPOET_JAVA)
}
0
在 gradle.properties 中添加 gradle 全局变量:
JAVAPOET_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/javapoet_java.jar
ORM_ANNOTATIONS_PROCESSOR_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/orm_annotations_processor_java.jar
ORM_ANNOTATIONS_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/orm_annotations_java.jar
TrainStore.java:
package com.example.phone.store;
import com.example.phone.store.from.Train;
import ohos.data.orm.OrmDatabase;
import ohos.data.orm.annotation.Database;
@Database(entities = {Train.class}, version = 1)
public abstract class TrainStore extends OrmDatabase {
}
Train.java:
package com.example.phone.store.from;
import ohos.data.orm.OrmObject;
import ohos.data.orm.annotation.Entity;
import ohos.data.orm.annotation.PrimaryKey;
import ohos.utils.zson.ZSONArray;
@Entity(tableName = "trail")
public class Train extends OrmObject {
@PrimaryKey(autoGenerate = true)
private Long id;
private String trainList;
public Train() {
}
public Train(Long id, String trainList) {
this.id = id;
this.trainList = trainList;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String toString() {
return "Train{" +
"id=" + id +
", trainList=" + trainList +
'}';
}
public String getTrainList() {
return trainList;
}
public void setTrainList(String trainList) {
this.trainList = trainList;
}
}
小卡片 Ability
主要作用时对小卡片的创建删除进行管理,点击事件的处理。
在创建小卡片时,在数据库中新建一条小卡片数据,当点击更新按钮时,从数据库里面读取对应卡片的 trainNo 和 date,然后请求 API 获取数据。
TrainAbility.java:
package com.example.phone.ability.train;
import com.example.phone.store.OilStore;
import com.example.phone.store.TrainStore;
import com.example.phone.store.from.OilPrice;
import com.example.phone.store.from.Train;
import ohos.aafwk.ability.;
import ohos.aafwk.content.Intent;
import ohos.app.Context;
import ohos.data.DatabaseHelper;
import ohos.data.orm.OrmContext;
import ohos.data.orm.OrmPredicates;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.zson.ZSONArray;
import ohos.utils.zson.ZSONObject;
import okhttp3.;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.List;
public class TrainAbility extends Ability {
public static final int DEFAULT_DIMENSION_2X2 = 2;
public static final int INVALID_FORM_ID = -1;
private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, TrainAbility.class.getName());
private static OrmContext ormContext = null;
private DatabaseHelper helper = new DatabaseHelper(this);
OkHttpClient okHttpClient = new OkHttpClient();
public void create(Context context){
System.out.println("创建高铁数据库");
ormContext = helper.getOrmContext("TrainStore", "TrainStore.db", TrainStore.class);
}
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
stopAbility(intent);
}
@Override
protected ProviderFormInfo onCreateForm(Intent intent) {
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);
if(ormContext == null){
create(getContext());
}
// 数据库新建数据
Train train = new Train();
train.setId(formId);
boolean isSuccessed = ormContext.insert(train);
isSuccessed = ormContext.flush();
return null;
}
@Override
protected void onTriggerFormEvent(long formId, String message) {
OrmPredicates predicates = ormContext.where(Train.class);
predicates.equalTo("id", formId);
List<Train> trailList = ormContext.query(predicates);
Train trail = trailList.get(0);
ZSONObject trainData = ZSONObject.stringToZSON(trail.getTrainList());
System.out.println(trail);
// 查询数据
String date = trainData.getString("chooseDate");
String trainNo = trainData.getString("trainNo");
String url = "https://kyfw.12306.cn/otn/queryTrainInfo/query?leftTicketDTO.train_no="+trainNo+"&leftTicketDTO.train_date="+date+"&rand_code=";
System.out.println(url);
Call call = okHttpClient.newCall(new Request.Builder().url(url).build());
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String body = response.body().string();
ZSONObject data = new ZSONObject();
ZSONArray statinList = ZSONObject.stringToZSON(body).getZSONObject("data").getZSONArray("data");
data.put("stationList",statinList);
System.out.println(data);
FormBindingData formBindingData = new FormBindingData(data);
try {
if (!updateForm(formId, formBindingData)) {
}
} catch (FormException e) {
e.printStackTrace();
}
}
});
super.onTriggerFormEvent(formId, message);
}
}
小卡片编辑页面的开发
index.hml
配置页面 CSS 代码很少我就不贴了:
具体业务逻辑:
①当页面初始化时请求 API 获取全国站台的缩写代码(编号),因为请求的是 JS 文件,需要使用|隔开存入数组。
https://kyfw.12306.cn/otn/resources/js/framework/station_name.js
var station_names ='@bjb|北京北|VAP|beijingbei|bjb|0@bjd|北京东|BOP|b
②获取今日日期。
③获取所有输入框和选择器的数值。
④点击查询按钮时,提取 train_station_code。获取该站台所有列车数据。
⑤整理数据发送给 service ability。
index.js
import fetch from '@system.fetch'
import app from '@system.app'
var that;
const ABILITY_TYPE_EXTERNAL = 0;
const ACTION_SYNC = 0;
const CHOOSE = 1001;
var stationNameArray = [];
export const TrainAbility = {
choose: async function(data){
var action = {};
var sendUserData = data;
console.info(sendUserData);
action.bundleName = 'com.example.phone';
action.abilityName = 'com.example.phone.ability.train.TrainServiceAbility';
action.messageCode = CHOOSE;
action.data = sendUserData;
action.abilityType = ABILITY_TYPE_EXTERNAL;
action.syncOption = ACTION_SYNC;
var result = await FeatureAbility.callAbility(action);
}
}
export default {
data: {
title: "",
today: "2000-01-01",
chooseDate:"",
seatPickerList:["商务座","一等座","二等座","站票"],
seatValue: "二等座",
pickerDefineIndex: 0,
trainNumber: "",
vehicleNumber: "",
seatNumber: "",
address: "",
trainNo:"",
trainData:{}
},
onInit() {
that = this;
//设置今日今天的时间
var nowDay = new Date();
let mount = (nowDay.getMonth()+1);
let day = nowDay.getDate();
if(mount < 10) mount = "0"+mount;
if(day < 10) day = "0"+day;
this.today = nowDay.getFullYear()+"-" + mount + "-" + day;
this.chooseDate = this.today;
// 请求获取站编号
fetch.fetch({
url: "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js",
success: function(response) {
var result = response.data;
stationNameArray = result.split('|');
},
fail: function() {
console.info("---get station_name fetch fail---");
}
});
},
addressChange(value){
this.address = value.text;
},
DataChange(e){
let mount = e.month+1;
let day = e.day;
if(mount < 10) mount = "0"+mount;
if(day < 10) day = "0"+day;
this.today = e.year + "-" + mount + "-" + day;
},
seatChange(obj){
this.seatValue = this.seatPickerList[obj.newSelected];
},
TrainNumberChange(value){
this.trainNumber = value.text;
},
VehicleNumberChange(value){
this.vehicleNumber = value.text;
},
SeatNumberChange(value){
this.seatNumber = value.text;
},
search(){
// 查询车架号https://kyfw.12306.cn/otn/czxx/query?train_start_date=2021-07-17&train_station_code=CAU
// 1.提取train_station_code
let index = stationNameArray.indexOf(this.address);
if(index == 0){
// 输入错误
return;
}
let addressCode = stationNameArray[index+1];
// 2.获取该站台所有列车数据 C3863 54000C386601
//
fetch.fetch({
url: "https://kyfw.12306.cn/otn/czxx/query?train_start_date="+that.chooseDate+"&train_station_code="+addressCode,
success: function(response) {
var result = JSON.parse(response.data);
if(result.data == null){
return;
}
// 查询trainNumber
let data = result.data.data;
for(var dindex in data){
if(data[dindex].station_train_code == that.trainNumber){
that.trainData = data[dindex];
that.trainNo = data[dindex].train_no;
break;
}
}
console.info("车数据:"+that.trainNo);
},
fail: function() {
console.info("https://kyfw.12306.cn/otn/czxx/query fail");
}
});
},
qr(){
if(that.trainNo == null || that.trainNo.length == 0){
console.info("数据有误");
return;
}
// 整理数据
let SendData = {
treainData:{
"seat": that.seatValue,
"today": that.today,
"trainNumber": that.trainNumber,
"vehicleNumber": that.vehicleNumber,
"seatNumber": that.seatNumber,
"trainNo": that.trainNo,
"chooseDate": that.chooseDate,
"startAddress": that.trainData.start_station_name,
"endAddress": that.trainData.end_station_name,
"startTime": that.trainData.start_start_time,
"endTime": that.trainData.end_arrive_time,
"trainDataString": that.today+" "+that.trainNumber+"\n"+that.seatValue+" "+that.vehicleNumber+" "+that.seatNumber
}
}
// 调试打印
console.info(this.trainData.toString());
// 发送数据给后端java service ability
TrainAbility.choose(SendData);
// 关闭当前页面
app.terminate();
}
}
编辑页面数据处理
TrainServiceAbility.java:
package com.example.phone.ability.train;
import com.example.phone.ability.OilConfigAbility;
import com.example.phone.store.OilStore;
import com.example.phone.store.TrainStore;
import com.example.phone.store.from.OilPrice;
import com.example.phone.store.from.Train;
import com.example.phone.utils.TrainDataTF;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.FormBindingData;
import ohos.aafwk.ability.FormException;
import ohos.aafwk.content.Intent;
import ohos.data.DatabaseHelper;
import ohos.data.orm.OrmContext;
import ohos.data.orm.OrmPredicates;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.rpc.*;
import ohos.utils.zson.ZSONArray;
import ohos.utils.zson.ZSONObject;
import java.util.List;
public class TrainServiceAbility extends Ability {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
private static final int CHOOSE = 1001;
private static OrmContext ormContext = null;
private DatabaseHelper helper = new DatabaseHelper(this);
private TrainServiceAbility.TrainServiceRemote trainServiceRemote;
@Override
public void onStart(Intent intent) {
HiLog.info(LABEL_LOG, "TrainServiceAbility::onStart");
trainServiceRemote = new TrainServiceAbility.TrainServiceRemote();
ormContext = helper.getOrmContext("TrainStore", "TrainStore.db", TrainStore.class);
super.onStart(intent);
}
@Override
protected IRemoteObject onConnect(Intent intent) {
super.onConnect(intent);
return trainServiceRemote.asObject();
}
@Override
public void onDisconnect(Intent intent) {
}
class TrainServiceRemote extends RemoteObject implements IRemoteBroker {
public TrainServiceRemote() {
super("TrainServiceRemote");
}
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) throws RemoteException {
switch (code) {
case CHOOSE:{
// 更新数据库
ZSONObject zsonStr = ZSONObject.stringToZSON(data.readString());
System.out.println(zsonStr);
OrmPredicates predicates = ormContext.where(Train.class);
predicates.equalTo("id", TrainConfigAbility.cardId);
List<Train> trailList = ormContext.query(predicates);
Train trail = trailList.get(0);
trail.setTrainList(zsonStr.getZSONObject("treainData").toString());
ormContext.update(trail);
ormContext.flush();
// 更新小卡片
FormBindingData formBindingData = new FormBindingData(zsonStr);
try {
if (!updateForm(TrainConfigAbility.cardId, formBindingData)) {
}
} catch (FormException e) {
e.printStackTrace();
}
break;
}
default: {
reply.writeString("service not defined");
return false;
}
}
return true;
}
@Override
public IRemoteObject asObject() {
return this;
}
}
}
- 分享
- 举报
-
浏览量:3643次2021-07-14 14:13:47
-
浏览量:5320次2021-07-14 14:09:54
-
浏览量:4131次2021-09-01 17:51:02
-
浏览量:4664次2021-06-28 14:52:06
-
浏览量:3923次2021-08-19 14:01:30
-
浏览量:3807次2021-09-04 16:38:41
-
浏览量:4428次2021-08-16 11:02:13
-
浏览量:4191次2021-07-26 14:22:03
-
浏览量:2833次2021-07-13 16:01:37
-
浏览量:5634次2021-08-04 13:46:28
-
浏览量:3871次2021-08-23 15:13:18
-
浏览量:7268次2020-12-10 14:24:24
-
浏览量:7547次2020-12-10 10:49:36
-
浏览量:4343次2021-06-30 09:47:29
-
浏览量:4251次2021-09-09 13:55:49
-
浏览量:1903次2022-03-01 09:00:11
-
浏览量:1710次2022-02-17 09:00:18
-
浏览量:1377次2019-01-16 09:46:12
-
浏览量:4614次2021-07-08 14:45:19
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
林家大哥
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明