1 前言
本次实验将上次没完成的设置页面做一个完善,然后使用preferences用户首选项保存host 和 apikey,然后完善一点细节,包括加载状态显示、启动应用自动获取数据、Toast提示和popup提示。
2 preferences 用户首选项
2.1 介绍
用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。
数据存储形式为键值对,键的类型为字符串型,值的存储数据类型包括数字型、字符型、布尔型以及这3种类型的数组类型。
用户首选项的持久化文件存储在preferencesDir路径下,创建preferences对象前,需要保证preferencesDir路径可读写。持久化文件存储路径中的加密等级会影响文件的可读写状态,路径访问限制详见应用文件目录与应用文件路径。
2.2 代码实现
在ets文件夹下新建database目录,里面再新建LocalStorage.ets,用于存放数据交互API
import { preferences } from "@kit.ArkData"
import CommonConstants from "../constant/CommonConstants"
export const DATABASE_NAME = 'settings'
export const APIKEY = 'api_key'
export const HOST = 'host'
export default class LocalStorage {
pref: preferences.Preferences | null = null
private async getPreferences() {
if (!this.pref) {
let pref = await preferences.getPreferences(getContext(), DATABASE_NAME)
this.pref = pref
}
return this.pref
}
public async setApiKey(apikey: string) {
let pref = await this.getPreferences()
let result = await pref.put(APIKEY, apikey);
pref.flush((err) => {
if (err) {
return
}
})
return result
}
public async getApiKey() {
let pref = await this.getPreferences()
let apikey = await pref.get(APIKEY, CommonConstants.apikey_default);
return apikey;
}
public async setHost(apikey: string) {
let pref = await this.getPreferences()
let result = await pref.put(HOST, apikey);
pref.flush((err) => {
if (err) {
return
}
})
return result
}
public async getHost() {
let pref = await this.getPreferences()
let apikey = await pref.get(HOST, CommonConstants.host_default);
return apikey;
}
}
3 完善settings设置页面
3.1 设置页面设计说明
设置页面主要放两部分,一部分是用两个输入框实现用户手动设置APIkey和设置host,另一部分是放两个按钮,保存和重置。
3.2 代码实现
@Preview({
title: 'settings'
})
@Component
export struct Settings {
pageStack: NavPathStack = new NavPathStack()
@State apikey: string = ''
@State host: string = ''
localStorage: LocalStorage = new LocalStorage()
build() {
NavDestination() {
Column() {
Row() {
Column() {
Text('和风天气\\nAPI Key')
.fontSize(15)
}
.width('30%')
Column() {
TextInput({ text: this.apikey ,placeholder:'请输入和风天气的API Key'})
.type(InputType.Email)
.onChange((value: string) => {
this.apikey = value
})
}
.width('65%')
}
.height(70)
.backgroundColor(Color.White)
.width('90%')
.borderRadius(15)
.margin({top:10,bottom:10})
Row() {
Column() {
Text('和风天气\\nhost')
.fontSize(15)
}
.width('30%')
Column() {
TextInput({ text: this.host })
.type(InputType.Email)
.onChange((value: string) => {
this.host = value
})
}
.width('65%')
}
.height(70)
.backgroundColor(Color.White)
.width('90%')
.borderRadius(15)
.margin({bottom:10})
Row() {
Button('保存')
.onClick(() => {
this.localStorage.setApiKey(this.apikey)
this.localStorage.setHost(this.host)
this.getUIContext().getPromptAction().showToast({
message: "保存成功!",
duration: 2000,
bottom: 85
})
})
.width(70)
Button('重置')
.onClick(() => {
this.apikey = CommonConstants.apikey_default
this.host = CommonConstants.host_default
this.localStorage.setApiKey(CommonConstants.apikey_default)
this.localStorage.setHost(CommonConstants.host_default)
this.getUIContext().getPromptAction().showToast({
message: "重置成功!",
duration: 2000,
bottom: 85
})
})
.width(70)
}
.width('80%')
.justifyContent(FlexAlign.SpaceAround)
}
}
.backgroundColor('#ffdee0e0')
.mode(NavDestinationMode.STANDARD)
.title('设置')
}
aboutToAppear(): void {
this.localStorage.getApiKey().then(apikey => {
this.apikey = apikey as string
})
this.localStorage.getHost().then(host => {
this.host = host as string
})
}
}
@Builder
export function PageSettingsBuilder(name: string, param: Object) {
Settings()
}
4 启动应用自动获取数据
4.1 生命周期简介
这里的启动应用自动获取数据,是用到了一个aboutToAppear函数实现的。aboutToAppear函数在创建自定义组件的新实例后,在执行其build()函数之前执行。允许在aboutToAppear函数中改变状态变量,更改将在后续执行build()函数中生效。实现自定义布局的自定义组件的aboutToAppear生命周期在布局过程中触发。
自定义组件的生命周期如下图所示

4.2 代码实现
nowWeatherDataComponent.ets文件中,struct nowWeatherDataComponent 内添加代码。
aboutToAppear(): void {
this.initData()
}
5 实现点击组件popup气泡
以点击体感温度小组件为例,使用onClick函数,执行匿名函数修改气泡文本以及控制气泡显示。
Row() {
Column({ space: 5 }) {
Row() {
Text('体感温度')
.fontSize(15)
.fontColor(Color.Gray)
Image($r('app.media.icon_feeling'))
.width(16)
.height(16)
}
.padding({ top: 20 })
Text(this.weatherUiModel.nowFeelTemp.toString() + '°')
.fontSize(25)
.padding({ top: 5 })
}
.backgroundColor(Color.White)
.borderRadius(15)
.width('48%')
.height(90)
.alignItems(HorizontalAlign.Start)
.padding({ left: 15 })
.onClick(() => {
let ft = this.weatherUiModel.nowFeelTemp
if (ft >= 30) {
this.feelingTempPopupMessage = '热热热!'
} else if (ft < 30 && ft >= 20) {
this.feelingTempPopupMessage = '体感舒适'
} else if (ft < 20 && ft >= 10) {
this.feelingTempPopupMessage = '体感凉爽'
} else if (ft < 10 && ft >= 0) {
this.feelingTempPopupMessage = '体感寒冷'
} else {
this.feelingTempPopupMessage = '冷冷冷!'
}
this.feelingTempPopup = !this.feelingTempPopup
})
.bindPopup(this.feelingTempPopup, {
message: this.feelingTempPopupMessage,
onStateChange: (e) => {
if (!e.isVisible) {
this.feelingTempPopup = false;
}
}
})
对于相对湿度组件,以类似的方法实现,见视频演示,在这就不再赘述。
6 实现网络连通性检测及数据更新状态
6.1 网络连通性检测
这里需要用到第三方包'@ohos-rs/ping'。新建一个pingApi.ets文件,放在network/api文件夹下
import {pingAsync} from '@ohos-rs/ping'
export async function testNetwork(){
try{
const data = await pingAsync("www.baidu.com", {
count: 1,
timeout: 1,
interval: 1,
ipVersion: 'auto',
});
console.log(`连接成功ping:${JSON.stringify(data)}`);
return true
}
catch (e) {
console.error('连接失败ping', e);
return false
}
}
在nowWeatherDataComponent组件中的initData函数进行判断,如果读取失败,会使用showToast方法给用户提示。
if (this.weatherNow.code != 200 || weather24h.code != 200 || weather7d.code != 200) {
this.readDataSuccess = false
this.dataLoadStatus = 2
this.getUIContext().getPromptAction().showToast({
message: "数据获取失败,请检查API设置!",
duration: 2000,
bottom: 85
})
return false
}
6.2 显示数据更新状态
数据更新状态显示,由变量dataLoadStatus控制,0 读取成功 1正在读取 2读取错误。
当正在读取数据时,显示一个loading的进度条动画。
@State dataLoadStatus: number = 1;
Row({ space: 5 }) {
if (this.dataLoadStatus == 0) {
Text('数据更新于 ' + new Date().getHours() + ':' + new Date().getMinutes())
.fontSize(10)
.width(400)
} else if (this.dataLoadStatus == 1) {
Progress({ value: 0, total: 100, type: ProgressType.Ring })
.width(10).color(Color.Black)
.style({ strokeWidth: 5, status: ProgressStatus.LOADING })
Text('数据更新中')
.fontSize(10)
.width(100)
} else {
Text('数据更新失败,请重试')
.fontSize(10)
.width(400)
}
}
.margin({ left: '10%' })
.justifyContent(FlexAlign.Start)
.width('100%')
7 实机演示
进入APP,天气数据自动更新,弹出更新成功提示框

点击相对湿度组件,根据当前湿度弹出不同的气泡提示

进入设置页面,故意将api key修改为一个错误值,保存


由于APIkey错误,提示数据更新失败

进入设置页面重置后,使用下拉列表切换别的城市,无需再次点击查询,将自动更新当前选择城市的数据
