鸿蒙5.0项目开发——鸿蒙天气项目的实现(欢迎页)
【高心星出品】
文章目录
- 欢迎页面效果
- 数据字典
- 创建数据库表格
- Splash页面
- 页面功能
- 欢迎页代码
- 亮点
项目按照从数据库连接层–视图层–业务逻辑层这种三层架构开发,所以先设计了数据库表格的结构,在EntryAbility中创建表格。
欢迎页面效果
数据字典
- searchmodel的数据字典:
/***** 搜索历史记录模型*** 用于存储用户搜索关键词的历史记录***/*export interface SearchHistoryItem {id?: number *// 记录ID,可选*keyword: string *// 搜索关键词*timestamp?: string *// 搜索时间戳,可选*}
- adcode的数据字典:
/***** 城市编码模型*** 用于存储城市相关的编码信息***/*export interface adcode {id: number *// 城市ID*pid?: number *// 父级ID,可选*city_code: string *// 城市编码*city_name: string *// 城市名称*post_code?: string *// 邮政编码,可选*area_code?: string *// 区号,可选*ctime?: string *// 创建时间,可选*}
这两个模型的主要用途:
SearchHistoryItem:
-
用于记录用户的搜索历史
-
包含搜索关键词和时间信息
-
可用于实现搜索历史功能
adcode:
-
用于存储城市信息
-
包含城市编码、名称等基本信息
-
支持城市层级关系(通过pid)
-
包含邮政和区号等附加信息
创建数据库表格
编写DbUtils实现对于表格的创建。
-
创建名为 weatherinfo.db 的数据库
-
设置数据库安全级别为 S1
-
初始化两个数据表:
-
t_adcode:存储城市信息
-
t_search:存储搜索历史
import { relationalStore } from "@kit.ArkData";/***作者:gxx*时间:2025/4/21 9:16*功能:数据库操作**/
// 城市编码表名
export const TABLENAME: string = 't_adcode'// 搜索历史表名
export const TABLENAME1: string = 't_search'// 数据库配置
const CONFIG: relationalStore.StoreConfig = {name: 'weatherinfo.db', // 数据库名称securityLevel: relationalStore.SecurityLevel.S1 // 安全级别
}/*** 获取数据库实例* @param context 上下文对象* @returns 数据库实例*/
export async function getrdb(context: Context) {return relationalStore.getRdbStore(context, CONFIG)
}/*** 创建数据库表* @param context 上下文对象* @description 创建城市编码表和搜索历史表*/
export async function createtable(context: Context) {try {// 创建城市编码表SQL语句let sql = 'CREATE TABLE IF NOT EXISTS t_adcode (\n' +' id INTEGER PRIMARY KEY AUTOINCREMENT,\n' + // 自增主键'name TEXT NOT NULL, code VARCHAR(50) NOT NULL,\n' + // 城市名称和编码' city VARCHAR(50) \n' + // 所属城市');'// 创建搜索历史表SQL语句let sql1 = 'CREATE TABLE IF NOT EXISTS t_search (\n' +' id INTEGER PRIMARY KEY AUTOINCREMENT,\n' + // 自增主键'keyword TEXT NOT NULL, timestamp VARCHAR(50) NOT NULL);' // 搜索关键词和时间戳// 获取数据库实例并执行SQLlet rdb = await getrdb(context)rdb.executeSync(sql)rdb.executeSync(sql1)} catch (e) {console.error('dbutils 创建表格失败: ', JSON.stringify(e))}
}
在EntryAbility中创建表格。
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { createtable } from '../utils/Dbutils';const DOMAIN = 0x0000;export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');}onDestroy(): void {hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');}async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {// 1. 记录窗口创建日志hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');// 2. 创建数据库表格// 创建adcode和search_history两个表格用于存储城市代码和搜索历史await createtable(this.context)// 3. 加载启动页面// 加载splash页面,splash页面会检查是否已导入城市数据// 如果未导入则会从cityma.txt读取并导入数据库// 导入完成后跳转到主页面IndexwindowStage.loadContent('pages/splash', async (err) => {if (err.code) {// 4. 加载失败处理hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));return;}// 5. 加载成功记录hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');});}onWindowStageDestroy(): void {// Main window is destroyed, release UI related resourceshilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');}onForeground(): void {// Ability has brought to foregroundhilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');}onBackground(): void {// Ability has back to backgroundhilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');}
}
Splash页面
页面功能
1.数据初始化:
-
页面启动时会检查是否已经加载过城市数据(通过 PreferenceUtils 存储的 isload 标志)
-
如果数据未加载,会执行 charu 函数来初始化数据
2.charu 函数的具体工作:
-
从资源文件中读取 cityma.txt(城市码数据文件)
-
将文件内容解析为 JSON 数据
-
通过 adcodedao 将城市数据插入到数据库中
-
使用 @Concurrent 注解确保在后台线程执行,避免阻塞主线程
3.界面展示:
-
显示一个启动图片
-
展示一个环形进度条动画
-
显示"加载中…"文字
4.页面跳转:
- 数据加载完成后,会自动跳转到主页面(pages/Index)
欢迎页代码
import { adcodedao } from '../dao/adcodedao';
import { buffer, taskpool } from '@kit.ArkTS';
import { common } from '@kit.AbilityKit';
import { adcode } from '../model/adcode';
import { PreferenceUtils } from '../utils/PreferenceUtils';
import { router } from '@kit.ArkUI';/*** 插入城市数据到数据库* @param context 上下文对象* @returns 插入是否成功*/
@Concurrent
async function charu(context: Context) {try {// 创建数据访问对象let ad = new adcodedao(context)// 读取原始文件内容let value = await context.resourceManager.getRawFileContent('cityma.txt')// 转换为字符串let data = buffer.from(value).toString()// 解析JSON数据let content = JSON.parse(data) as adcode[]// 插入数据库await ad.insert(content)return true} catch (e) {console.error('gxxt ', JSON.stringify(e))return false}
}// 持久化存储的key名称
const Prename: string = 'loaded'/*** 启动页面组件*/
@Entry
@Component
struct Splash {@State message: string = 'Hello World';// 进度条值@State value: number = 10// 上下文对象private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext// 定时器IDprivate intervalid: number = 0/*** 组件即将出现时的生命周期函数*/aboutToAppear(): void {// 设置定时器更新进度条this.intervalid = setInterval(() => {if (this.value == 100) {this.value = -10}this.value += 10}, 100)// 检查是否已加载过数据let loaded = PreferenceUtils.getInstance(this.context, Prename).get('isload', false) as booleanif (loaded) {// 如果已加载过数据,直接跳转到主页router.replaceUrl({ url: 'pages/Index' })} else {// 如果未加载过数据,则读取文件并插入数据库taskpool.execute(charu, this.context).then((value) => {// 保存加载状态PreferenceUtils.getInstance(this.context, Prename).putfile('isload', value as boolean)// 跳转到主页router.replaceUrl({ url: 'pages/Index' })})}}/*** 构建UI界面*/build() {Stack() {// 背景图片Image($r('app.media.splash'))// 进度条Progress({ value: this.value, total: 100, type: ProgressType.ScaleRing }).width(100).height(100).backgroundColor(Color.Black).style({ strokeWidth: 15, scaleCount: 20, scaleWidth: 5 }).color(Color.White)// 加载提示文本Text('加载中...').fontWeight(FontWeight.Bolder)}.height('100%').width('100%').alignContent(Alignment.Center)}/*** 组件即将消失时的生命周期函数*/aboutToDisappear(): void {// 清除定时器clearInterval(this.intervalid)}
}
亮点
1.使用多线程将大量数据批量插入数据库
批量插入:
/*** 批量插入城市数据到数据库* @param acs 城市数据数组* @description 将城市数据批量插入到数据库中* 如果城市数据包含ctime字段,则插入name、code、city三个字段* 否则只插入name和code两个字段*/async insert(acs: adcode[]) {try {// 获取数据库实例let rdb = await getrdb(this.context)// 存储要插入的数据let values: relationalStore.ValuesBucket[] = []// 遍历城市数据acs.forEach((ac: adcode) => {let value: relationalStore.ValuesBucket// 判断是否有ctime字段if (ac.ctime) {value = {'name': ac.city_name, // 城市名称'code': ac.city_code, // 城市编码'city': ac.ctime // 所属城市}} else {value = {'name': ac.city_name, // 城市名称'code': ac.city_code, // 城市编码}}values.push(value)})// 批量插入数据rdb.batchInsertSync(TABLENAME, values)} catch (e) {console.error('gxxt adcodedao 插入数据错误: ', e.message)}}
多线程运行实体:
@Concurrent
async function charu(context: Context) {try {// 创建数据访问对象let ad = new adcodedao(context)// 读取原始文件内容let value = await context.resourceManager.getRawFileContent('cityma.txt')// 转换为字符串let data = buffer.from(value).toString()// 解析JSON数据let content = JSON.parse(data) as adcode[]// 插入数据库await ad.insert(content)return true} catch (e) {console.error('gxxt ', JSON.stringify(e))return false}
}
多线程执行:
// 如果未加载过数据,则读取文件并插入数据库taskpool.execute(charu, this.context).then((value) => {// 保存加载状态PreferenceUtils.getInstance(this.context, Prename).putfile('isload', value as boolean)// 跳转到主页router.replaceUrl({ url: 'pages/Index' })})
2.使用Interval配合环形刻度进度条来制作动态的加载动画
// 设置定时器更新进度条this.intervalid = setInterval(() => {if (this.value == 100) {this.value = -10}this.value += 10}, 100)
....................................// 进度条Progress({ value: this.value, total: 100, type: ProgressType.ScaleRing }).width(100).height(100).backgroundColor(Color.Black).style({ strokeWidth: 15, scaleCount: 20, scaleWidth: 5 }).color(Color.White)