HarmonyOS NEXT 企业级记账APP:设置中心开发

发布时间:2026/8/2 4:05:02

HarmonyOS NEXT 企业级记账APP:设置中心开发
设置中心开发本文是《HarmonyOS NEXT 企业级开发实战30篇打造智能记账APP》系列的第24篇对应 Git Tagv0.2.4。承接前序开发本篇开发SettingView设置中心支持深色模式切换、数据导出、清空数据与关于页面跳转等能力底层基于SettingRepository与PreferenceUtil完成偏好持久化。前言企业级应用的可配置能力与数据掌控能力决定产品成熟度。一个完善的设置中心不仅要让用户自定义外观偏好如深色模式还要让用户能够导出和清理自己的数据。本文将带你设计SettingRepository设置仓库与PreferenceUtil偏好存储工具实现SettingView设置页面的完整布局与交互封装ConfirmDialog确认对话框处理危险操作集成RouterUtil路由工具完成页面跳转遵循 ArkTS 类型安全规范杜绝any类型企业级核心原则功能必须完整、可恢复、类型安全。ArkTS 严格禁止使用any类型所有类名必须使用英文标识符。参考 HarmonyOS NEXT 开发者文档 了解官方约定。一、需求分析1.1 功能介绍设置中心是用户管理应用偏好与数据的统一入口。本篇实现的SettingView包含四项核心配置全部基于List组件构建分组列表。需求项说明深色模式通过Toggle开关切换深色模式持久化到SettingRepository重启后生效导出数据将账单与分类数据序列化为 JSON 格式便于用户备份迁移清空数据危险操作通过ConfirmDialog二次确认后清空所有偏好数据关于页面跳转至AboutView展示应用名称、版本号与开源信息1.2 设置项规划设置项采用分组列表布局每项为独立ListItem通过Row容器横向排列标题与操作控件。设置项交互控件触发动作危险等级深色模式Toggle开关saveDarkMode持久化低导出数据箭头图标exportData序列化 JSON低清空所有数据红色文字弹出ConfirmDialog确认高关于箭头图标RouterUtil.push跳转低1.3 业务流程用户进入 SettingView ↓ aboutToAppear 调用 loadSettings ↓ SettingRepository.loadDarkMode 读取深色模式偏好 ↓ UI 渲染分组列表Toggle / 文字 / 箭头 ↓ 用户交互 → 更新 State → 持久化偏好 → Toast 反馈 ↓ 清空操作 → ConfirmDialog 二次确认 → PreferenceUtil.clear二、SettingRepository 设置仓库2.1 单例模式实现SettingRepository是设置数据的统一仓库层采用单例模式确保全局唯一实例。它封装了对PreferenceUtil的调用对外暴露主题、语言、币种、深色模式等配置的读写方法。// repository/SettingRepository.ets import { PreferenceUtil } from ../utils/PreferenceUtil; export class SettingRepository { private static instance: SettingRepository; static getInstance(): SettingRepository { if (!SettingRepository.instance) { SettingRepository.instance new SettingRepository(); } return SettingRepository.instance; } async saveTheme(mode: string): Promisevoid { await PreferenceUtil.getInstance().setString(setting_theme, mode); } async loadTheme(): Promisestring { return await PreferenceUtil.getInstance().getString(setting_theme, auto); } async saveLanguage(lang: string): Promisevoid { await PreferenceUtil.getInstance().setString(setting_language, lang); } async saveCurrency(currency: string): Promisevoid { await PreferenceUtil.getInstance().setString(setting_currency, currency); } async saveDarkMode(enabled: boolean): Promisevoid { await PreferenceUtil.getInstance().setBoolean(setting_dark_mode, enabled); } async loadDarkMode(): Promiseboolean { return await PreferenceUtil.getInstance().getBoolean(setting_dark_mode, false); } }2.2 偏好读写封装每个配置项对应一个独立的key通过PreferenceUtil的setString/getString或setBoolean/getBoolean完成读写。注意所有方法参数与返回值均有明确的类型标注不使用any。2.3 字段说明方法参数类型返回类型存储键用途saveThemestringPromisevoidsetting_theme保存主题模式loadTheme无Promisestringsetting_theme读取主题模式saveLanguagestringPromisevoidsetting_language保存语言设置saveCurrencystringPromisevoidsetting_currency保存币种设置saveDarkModebooleanPromisevoidsetting_dark_mode保存深色模式开关loadDarkMode无Promisebooleansetting_dark_mode读取深色模式开关三、PreferenceUtil 偏好存储工具3.1 初始化与单例PreferenceUtil是基于kit.ArkData的preferences模块封装的轻量存储工具。它需要在应用启动时调用init方法传入Context完成初始化之后即可全局调用。// utils/PreferenceUtil.ets import { preferences } from kit.ArkData; import { common } from kit.AbilityKit; const PREF_NAME harmonyledger; export class PreferenceUtil { private pref: preferences.Preferences | null null; private static instance: PreferenceUtil | null null; static getInstance(): PreferenceUtil { if (PreferenceUtil.instance null) { PreferenceUtil.instance new PreferenceUtil(); } return PreferenceUtil.instance; } async init(context: common.Context): Promisevoid { this.pref await preferences.getPreferences(context, PREF_NAME); } }3.2 基本类型读写PreferenceUtil提供了字符串、布尔、数字三种基本类型的读写方法每次写入后自动调用flush持久化到磁盘。async setString(key: string, value: string): Promisevoid { if (this.pref null) return; await this.pref.put(key, value); await this.pref.flush(); } async getString(key: string, defaultValue: string ): Promisestring { if (this.pref null) return defaultValue; let result: ESObject await this.pref.get(key, defaultValue); return result; } async setBoolean(key: string, value: boolean): Promisevoid { if (this.pref null) return; await this.pref.put(key, value); await this.pref.flush(); } async getBoolean(key: string, defaultValue: boolean false): Promiseboolean { if (this.pref null) return defaultValue; let result: ESObject await this.pref.get(key, defaultValue); return result true || result true; }3.3 对象读写与清空除基本类型外PreferenceUtil还支持泛型对象的序列化存储以及delete单项删除和clear全量清空。SettingView的清空数据功能正是调用clear方法。async setObjectT(key: string, value: T): Promisevoid { if (this.pref null) return; const json JSON.stringify(value); await this.pref.put(key, json); await this.pref.flush(); } async delete(key: string): Promisevoid { if (this.pref null) return; await this.pref.delete(key); await this.pref.flush(); } async clear(): Promisevoid { if (this.pref null) return; await this.pref.clear(); await this.pref.flush(); }3.4 API 说明方法参数返回值说明initcontext: common.ContextPromisevoid初始化偏好实例setStringkey, valuePromisevoid写入字符串getStringkey, defaultValuePromisestring读取字符串setBooleankey, valuePromisevoid写入布尔值getBooleankey, defaultValuePromiseboolean读取布尔值setObjectTkey, value: TPromisevoid写入泛型对象clear无Promisevoid清空所有偏好四、SettingView 页面实现4.1 页面状态设计SettingView使用两个State状态变量管理页面数据。没有使用独立的 ViewModel业务逻辑直接调用SettingRepository与PreferenceUtil因为设置页面的状态非常简单无需引入额外的抽象层。State darkMode: boolean false; // 深色模式开关状态 State showClearConfirm: boolean false; // 清空确认对话框显示状态4.2 完整页面代码以下是SettingView的完整实现包含顶栏、分组列表、确认对话框三部分。所有代码与实际源码完全一致。// pages/SettingView.ets import { AppColors } from ../theme/Colors; import { AppFontSize } from ../theme/Typography; import { AppSpace } from ../theme/Spacing; import { RouterUtil } from ../utils/RouterUtil; import { SettingRepository } from ../repository/SettingRepository; import { PreferenceUtil } from ../utils/PreferenceUtil; import { ToastUtil } from ../utils/ToastUtil; import { ConfirmDialog } from ../components/dialog/ConfirmDialog; Entry Component struct SettingView { State darkMode: boolean false; State showClearConfirm: boolean false; aboutToAppear(): void { this.loadSettings(); } private async loadSettings(): Promisevoid { this.darkMode await SettingRepository.getInstance().loadDarkMode(); } build() { Column() { Row() { Image($r(app.media.icon_back)).width(24).height(24).fillColor(AppColors.PrimaryText) .onClick(() { RouterUtil.back(); }) Text(设置).fontSize(AppFontSize.XL).fontWeight(FontWeight.Bold).layoutWeight(1).textAlign(TextAlign.Center) }.width(100%).height(56).alignItems(VerticalAlign.Center) List({ space: AppSpace.SM }) { // 深色模式 ListItem() { Row() { Text(深色模式).fontSize(AppFontSize.MD).fontColor(AppColors.PrimaryText).layoutWeight(1) Toggle({ type: ToggleType.Switch, isOn: this.darkMode }) .onChange((isOn: boolean) { this.darkMode isOn; SettingRepository.getInstance().saveDarkMode(isOn); ToastUtil.show(重启应用后生效); }) } .width(100%).height(56) .padding({ left: AppSpace.MD, right: AppSpace.MD }) .backgroundColor(AppColors.CardBackground).borderRadius(AppSpace.CardRadius) } // 数据导出 ListItem() { Row() { Text(导出数据).fontSize(AppFontSize.MD).fontColor(AppColors.PrimaryText).layoutWeight(1) Image($r(app.media.icon_arrow_right)).width(20).height(20).fillColor(AppColors.SecondaryText) } .width(100%).height(56) .padding({ left: AppSpace.MD, right: AppSpace.MD }) .backgroundColor(AppColors.CardBackground).borderRadius(AppSpace.CardRadius) .onClick(() { this.exportData(); }) } // 清空数据 ListItem() { Row() { Text(清空所有数据).fontSize(AppFontSize.MD).fontColor(AppColors.Expense).layoutWeight(1) } .width(100%).height(56) .padding({ left: AppSpace.MD, right: AppSpace.MD }) .backgroundColor(AppColors.CardBackground).borderRadius(AppSpace.CardRadius) .onClick(() { this.showClearConfirm true; }) } // 关于 ListItem() { Row() { Text(关于).fontSize(AppFontSize.MD).fontColor(AppColors.PrimaryText).layoutWeight(1) Image($r(app.media.icon_arrow_right)).width(20).height(20).fillColor(AppColors.SecondaryText) } .width(100%).height(56) .padding({ left: AppSpace.MD, right: AppSpace.MD }) .backgroundColor(AppColors.CardBackground).borderRadius(AppSpace.CardRadius) .onClick(() { RouterUtil.push(pages/AboutView); }) } } .layoutWeight(1).margin({ top: AppSpace.MD }) if (this.showClearConfirm) { ConfirmDialog({ title: 确认清空, message: 清空后将删除所有账单、分类和预算数据此操作不可恢复, confirmText: 清空, confirmColor: AppColors.Expense, onConfirm: () { this.doClear(); }, onCancel: () { this.showClearConfirm false; } }) } } .height(100%).padding({ left: AppSpace.XL, right: AppSpace.XL, top: AppSpace.MD }) .backgroundColor(AppColors.Background) } private async exportData(): Promisevoid { const billsJson await PreferenceUtil.getInstance().getString(bills, []); const categoriesJson await PreferenceUtil.getInstance().getString(categories, []); interface ExportData { bills: string; categories: string; exportTime: string; } const data: ExportData { bills: billsJson, categories: categoriesJson, exportTime: new Date().toISOString() }; const json JSON.stringify(data, null, 2); ToastUtil.show(数据已准备JSON格式); } private async doClear(): Promisevoid { await PreferenceUtil.getInstance().clear(); this.showClearConfirm false; ToastUtil.show(数据已清空); } }4.3 设置项列表解析页面主体使用List({ space: AppSpace.SM })构建分组列表每个ListItem内部是一个Row容器深色模式左侧Text标题占据layoutWeight(1)右侧Toggle开关onChange回调中同步状态并持久化导出数据左侧标题 右侧箭头图标点击触发exportData方法清空数据文字使用AppColors.Expense红色警示点击弹出确认对话框关于标准箭头布局点击跳转pages/AboutView五、核心功能实现5.1 深色模式切换深色模式通过Toggle组件实现开关交互状态变化时立即调用SettingRepository.saveDarkMode持久化并通过ToastUtil提示用户重启生效。Toggle({ type: ToggleType.Switch, isOn: this.darkMode }) .onChange((isOn: boolean) { this.darkMode isOn; SettingRepository.getInstance().saveDarkMode(isOn); ToastUtil.show(重启应用后生效); })注意onChange回调参数isOn的类型明确标注为boolean不允许使用any。这是 ArkTS 类型安全的强制要求。5.2 数据导出exportData方法从PreferenceUtil读取账单与分类的 JSON 字符串组装为ExportData接口对象后序列化输出。关于数据导出的完整设计将在下一篇详细展开。private async exportData(): Promisevoid { const billsJson await PreferenceUtil.getInstance().getString(bills, []); const categoriesJson await PreferenceUtil.getInstance().getString(categories, []); interface ExportData { bills: string; categories: string; exportTime: string; } const data: ExportData { bills: billsJson, categories: categoriesJson, exportTime: new Date().toISOString() }; const json JSON.stringify(data, null, 2); ToastUtil.show(数据已准备JSON格式); }导出方法的执行步骤如下调用PreferenceUtil.getString异步读取bills和categories偏好数据定义ExportData接口约束导出数据结构组装包含账单、分类和导出时间的ExportData对象使用JSON.stringify序列化为格式化 JSON 字符串通过ToastUtil.show提示用户导出完成5.3 清空数据清空数据是高危操作必须经过ConfirmDialog二次确认。确认后调用PreferenceUtil.getInstance().clear()清空所有偏好数据。private async doClear(): Promisevoid { await PreferenceUtil.getInstance().clear(); this.showClearConfirm false; ToastUtil.show(数据已清空); }5.4 关于页面跳转关于页面通过RouterUtil.push跳转传递路由路径字符串pages/AboutView。.onClick(() { RouterUtil.push(pages/AboutView); })六、ConfirmDialog 确认对话框6.1 组件设计ConfirmDialog是通用的确认对话框组件通过Prop接收标题、消息、按钮文案与颜色通过回调函数onConfirm/onCancel处理用户操作。// components/dialog/ConfirmDialog.ets import { AppColors } from ../../theme/Colors; import { AppFontSize, AppFontWeight } from ../../theme/Typography; import { AppSpace } from ../../theme/Spacing; Component export struct ConfirmDialog { Prop title: string 确认; Prop message: string ; Prop confirmText: string 确认; Prop cancelText: string 取消; Prop confirmColor: string AppColors.Budget; onConfirm: () void () {}; onCancel: () void () {}; build() { Column() { Column() .width(100%).height(100%).backgroundColor(#80000000) .onClick(() { this.onCancel(); }) Column() { Text(this.title).fontSize(AppFontSize.LG).fontWeight(AppFontWeight.Bold).margin({ bottom: AppSpace.MD }) Text(this.message).fontSize(AppFontSize.MD).fontColor(AppColors.SecondaryText) .textAlign(TextAlign.Center).margin({ bottom: AppSpace.LG }) Row({ space: AppSpace.MD }) { Text(this.cancelText).layoutWeight(1).textAlign(TextAlign.Center) .padding({ top: 12, bottom: 12 }).backgroundColor(AppColors.Background).borderRadius(24) .onClick(() { this.onCancel(); }) Text(this.confirmText).layoutWeight(1).textAlign(TextAlign.Center).fontColor(#FFFFFF) .padding({ top: 12, bottom: 12 }).backgroundColor(this.confirmColor).borderRadius(24) .onClick(() { this.onConfirm(); }) }.width(100%) } .width(80%).padding(AppSpace.LG) .backgroundColor(AppColors.CardBackground).borderRadius(16) } .width(100%).height(100%).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center) } }6.2 在 SettingView 中的使用在SettingView中通过条件渲染if (this.showClearConfirm)控制对话框的显示与隐藏确认按钮使用红色AppColors.Expense突出危险操作。if (this.showClearConfirm) { ConfirmDialog({ title: 确认清空, message: 清空后将删除所有账单、分类和预算数据此操作不可恢复, confirmText: 清空, confirmColor: AppColors.Expense, onConfirm: () { this.doClear(); }, onCancel: () { this.showClearConfirm false; } }) }七、路由配置与页面注册7.1 main_pages.json 路由注册SettingView与AboutView都需要在main_pages.json中注册路由路径否则RouterUtil.push跳转时会报错。// entry/src/main/resources/base/profile/main_pages.json{src:[pages/MainView,pages/AddBillView,pages/EditBillView,pages/SearchView,pages/CategoryManageView,pages/SettingView,pages/AboutView]}7.2 RouterUtil 路由工具RouterUtil封装了kit.ArkUI的router模块提供push、pushWithId、back、replace四个静态方法统一处理异常捕获。// utils/RouterUtil.ets import { router } from kit.ArkUI; export interface RouteParams { id: string; } export class RouterUtil { static push(url: string): void { router.pushUrl({ url: url }).catch((err: Error) { console.error(Router push failed:, JSON.stringify(err)); }); } static pushWithId(url: string, id: string): void { let params: RouteParams { id: id }; router.pushUrl({ url: url, params: params }).catch((err: Error) { console.error(Router push failed:, JSON.stringify(err)); }); } static back(): void { try { router.back(); } catch (e) { console.error(Router back failed:, JSON.stringify(e)); } } static replace(url: string): void { router.replaceUrl({ url: url }).catch((err: Error) { console.error(Router replace failed:, JSON.stringify(err)); }); } }7.3 AboutView 关于页面AboutView是设置中心跳转的目标页面展示应用图标、名称、版本号与开源信息。// pages/AboutView.ets import { AppColors } from ../theme/Colors; import { AppFontSize } from ../theme/Typography; import { AppSpace } from ../theme/Spacing; import { RouterUtil } from ../utils/RouterUtil; Entry Component struct AboutView { build() { Column() { Row() { Image($r(app.media.icon_back)).width(24).height(24).fillColor(AppColors.PrimaryText) .onClick(() { RouterUtil.back(); }) Text(关于).fontSize(AppFontSize.XL).fontWeight(FontWeight.Bold).layoutWeight(1).textAlign(TextAlign.Center) }.width(100%).height(56).alignItems(VerticalAlign.Center) Column() { Image($r(app.media.icon_tab_user)).width(80).height(80).fillColor(AppColors.Budget) .margin({ top: 40 }) Text(HarmonyLedger).fontSize(24).fontColor(AppColors.PrimaryText).fontWeight(FontWeight.Bold) .margin({ top: AppSpace.LG }) Text(鸿蒙记账).fontSize(16).fontColor(AppColors.SecondaryText) .margin({ top: AppSpace.XS }) Text(v1.0.0).fontSize(14).fontColor(AppColors.SecondaryText) .margin({ top: AppSpace.SM }) Text(HarmonyOS NEXT 企业级记账 APP).fontSize(14).fontColor(AppColors.SecondaryText) .margin({ top: AppSpace.XL }) Text(基于 ArkTS ArkUI 开发).fontSize(14).fontColor(AppColors.SecondaryText) .margin({ top: AppSpace.XS }) Text(GitHub 开源项目).fontSize(14).fontColor(AppColors.Budget) .margin({ top: AppSpace.XL }) } .width(100%) .layoutWeight(1) .alignItems(HorizontalAlign.Center) } .height(100%).padding({ left: AppSpace.XL, right: AppSpace.XL, top: AppSpace.MD }) .backgroundColor(AppColors.Background) } }八、主题常量体系8.1 AppColors 颜色常量AppColors使用static readonly定义所有颜色 Token包括语义颜色收入绿、支出红、预算蓝、统计紫与中性颜色背景、卡片、文字。// theme/Colors.ets export class AppColors { // Base Token static readonly Green500: string #34C759; static readonly Red500: string #FF3B30; static readonly Blue500: string #007AFF; static readonly Purple500: string #AF52DE; // Semantic Token static readonly Income: string AppColors.Green500; static readonly Expense: string AppColors.Red500; static readonly Budget: string AppColors.Blue500; static readonly Statistic: string AppColors.Purple500; // Neutral static readonly Background: string #F2F2F7; static readonly CardBackground: string #FFFFFF; static readonly PrimaryText: string #1C1C1E; static readonly SecondaryText: string #8E8E93; static readonly Separator: string #E5E5EA; }8.2 AppFontSize 字体常量// theme/Typography.ets export class AppFontSize { static readonly XS: number 12; static readonly SM: number 14; static readonly MD: number 16; static readonly LG: number 18; static readonly XL: number 22; static readonly XXL: number 28; static readonly Display: number 36; }8.3 AppSpace 间距常量// theme/Spacing.ets export class AppSpace { static readonly XS: number 4; static readonly SM: number 8; static readonly MD: number 16; static readonly LG: number 20; static readonly XL: number 24; static readonly XXL: number 32; static readonly PagePadding: number 20; static readonly CardPadding: number 16; static readonly CardRadius: number 16; }8.4 主题常量对照表常量类代表含义示例值使用场景AppColors.Expense支出红#FF3B30清空数据警示文字AppColors.Budget预算蓝#007AFF关于页图标AppFontSize.XL大标题22顶栏标题AppFontSize.MD正文16列表项文字AppSpace.CardRadius卡片圆角16ListItem 圆角AppSpace.XL大间距24页面左右边距九、ArkTS 类型安全规范9.1 禁用 any 类型ArkTS 是基于 TypeScript 的强类型语言严格禁止使用any类型。any类型会绕过编译期类型检查导致运行时错误难以定位。在SettingView的实现中所有变量、参数、返回值都有明确的类型标注。9.2 接口定义代替 any当需要描述复杂对象结构时应使用interface定义接口而非依赖any。SettingView的exportData方法中定义了ExportData接口来约束导出数据结构。错误写法禁止// 违反 ArkTS 类型安全禁止使用 private async exportData(): Promisevoid { const data: any { bills: , categories: , exportTime: }; }正确写法推荐// 使用 interface 定义明确的类型约束 interface ExportData { bills: string; categories: string; exportTime: string; } const data: ExportData { bills: billsJson, categories: categoriesJson, exportTime: new Date().toISOString() };9.3 类名必须使用英文ArkTS 中类名、结构体名、接口名必须使用英文标识符。中文标识符在编译时会报错。场景错误写法正确写法仓库类名中文类名编译报错SettingRepository页面结构体名中文结构体名编译报错SettingView方法参数类型item: anyitem: ExportData列表数据类型data: Arrayanydata: ArrayExportDataForEach 键值函数(item: any) item.id(item: ExportData) item.exportTime9.4 异步方法返回类型所有async方法必须显式标注返回类型为PromiseT不能省略。SettingView中的三个异步方法均遵循此规范private async loadSettings(): Promisevoid { ... } private async exportData(): Promisevoid { ... } private async doClear(): Promisevoid { ... }十、运行验证10.1 构建命令hvigorw assembleHap--modemodule-pproductdefault10.2 验证清单验证项操作步骤预期结果进入设置页从主页点击设置入口展示四项设置列表深色模式切换点击 Toggle 开关开关状态变化Toast 提示重启应用后生效重启后保持切换后重启应用深色模式状态与切换前一致导出数据点击导出数据项Toast 提示数据已准备JSON格式清空确认弹窗点击清空所有数据弹出 ConfirmDialog 确认框取消清空在确认框点击取消对话框关闭数据不变确认清空在确认框点击清空数据清空Toast 提示数据已清空关于跳转点击关于项跳转至 AboutView 页面验证要点深色模式持久化需重启验证清空操作需确认数据确实被清除重新进入账单页应为空。十一、常见问题11.1 PreferenceUtil 未初始化现象调用getString/getBoolean时始终返回默认值数据无法持久化。原因PreferenceUtil的init方法未在应用启动时调用导致this.pref为null。解决在EntryAbility的onCreate中调用初始化// EntryAbility.ets export default class EntryAbility extends UIAbility { async onCreate(want, launchParam): Promisevoid { await PreferenceUtil.getInstance().init(this.context); } }11.2 深色模式切换不生效现象Toggle 开关切换后UI 颜色没有立即变化。原因当前实现仅持久化深色模式开关实际主题切换需重启应用后由ThemeManager读取偏好并应用。解决Toast 已提示重启应用后生效若需即时切换需在onChange中同时调用ThemeManager.switch并通过AppStorage全局刷新。11.3 清空数据误操作现象用户误触清空数据全部丢失。原因PreferenceUtil.clear()会清空所有偏好数据不可恢复。解决已通过ConfirmDialog二次确认防护。建议在确认对话框的文案中明确说明后果当前文案为清空后将删除所有账单、分类和预算数据此操作不可恢复。11.4 路由跳转失败现象点击关于项后页面无反应或报错。原因main_pages.json中未注册pages/AboutView路由。解决检查main_pages.json的src数组是否包含pages/AboutView。十二、Git 提交12.1 提交命令gitadd.gitcommit-mfeat(setting): 设置中心开发 - 新增 SettingRepository 设置仓库单例模式 - 新增 SettingView 设置页面深色模式/导出/清空/关于 - 集成 PreferenceUtil 完成偏好持久化 - 封装 ConfirmDialog 确认对话框处理危险操作 - 注册 SettingView 与 AboutView 路由12.2 变更记录## [v0.2.4] - 2026-07-28 ### Added - repository/SettingRepository.ets - pages/SettingView.ets - pages/AboutView.ets - components/dialog/ConfirmDialog.ets ### Changed - main_pages.json 新增 SettingView、AboutView 路由附录运行效果截图总结本文完整介绍了SettingView 设置中心的开发过程涵盖SettingRepository设置仓库、PreferenceUtil偏好存储工具、SettingView页面实现、ConfirmDialog确认对话框、RouterUtil路由跳转与主题常量体系。通过本篇你可以设计基于单例模式的设置仓库与偏好存储工具实现深色模式切换并持久化到本地偏好封装确认对话框处理高危清空操作集成路由工具完成页面跳转遵循 ArkTS 类型安全规范使用interface代替any类型下一篇预告第 25 篇将深入讲解数据导出、备份与恢复的完整方案基于SettingView中的exportData方法扩展为支持 JSON 导出、本地备份与数据恢复的全链路设计。如果这篇文章对你有帮助欢迎点赞、收藏、关注你的支持是我持续创作的动力欢迎在评论区留言讨论或参与下方投票告诉我你最想了解 HarmonyOS 开发的哪个方向相关资源本篇源码GitHub Tag v0.2.4HarmonyOS NEXT 开发者文档developer.harmonyos.comArkUI List 组件list 组件参考ArkUI Toggle 组件toggle 组件参考鸿蒙数据存储 preferencesdata-preferencesArkUI 路由管理router 导航ArkTS 类型安全规范arkts 类型约束ArkUI Prop 装饰器prop 状态管理CSV 格式规范RFC 4180

相关新闻

QTableWidget的ui运用

QTableWidget的ui运用

2026/8/2 4:05:02

1、Header属性2、QTableWidget属性3、基础QWidget属性4、QFrame属性QFrame 是 Qt 里面专门用来做边框、分割线、容器背景的控件5、QAbstractScrollArea 属性QAbstractScrollArea 是一个带滚动功能控件的基类6、QAbstractItemView属性重要,这些属性决定:表…

卷积神经网络变体全解析:从分组卷积到深度可分离卷积

卷积神经网络变体全解析:从分组卷积到深度可分离卷积

2026/8/2 4:05:01

1. 卷积操作的核心:从“普通”说起在计算机视觉和深度学习领域,卷积神经网络(CNN)是当之无愧的基石。而卷积操作本身,则是这块基石中最核心的运算单元。很多朋友在入门时,对“卷积”这个概念感到抽象&#…

深入解决NotepadNext在Windows 7系统上的兼容性挑战

深入解决NotepadNext在Windows 7系统上的兼容性挑战

2026/8/2 3:55:01

深入解决NotepadNext在Windows 7系统上的兼容性挑战 【免费下载链接】NotepadNext A cross-platform, reimplementation of Notepad 项目地址: https://gitcode.com/GitHub_Trending/no/NotepadNext NotepadNext作为Notepad的跨平台重实现,在Windows 7系统上…

Labelme JSON转Mask:语义分割数据预处理核心技术与实战

Labelme JSON转Mask:语义分割数据预处理核心技术与实战

2026/8/2 6:15:07

1. 项目概述:从标注文件到像素级掩码的转换在计算机视觉,特别是语义分割任务中,我们经常遇到一个看似简单却至关重要的环节:如何将标注工具(如Labelme)生成的JSON文件,转换成模型训练所需的Mask…

低成本打造7英寸副屏:硬件选型、软件配置与效率提升全攻略

低成本打造7英寸副屏:硬件选型、软件配置与效率提升全攻略

2026/8/2 6:15:07

1. 项目概述:为什么你需要一块副屏?如果你和我一样,每天面对着一块主显示器,同时开着代码编辑器、浏览器、通讯软件、终端和性能监控面板,那么你一定经历过那种在十几个窗口间疯狂切换的焦躁感。主屏幕再大&#xff0c…

Spring AI 系列(三):RAG 开发必知——两种 Advisor 到底怎么选?附完整代码实现

Spring AI 系列(三):RAG 开发必知——两种 Advisor 到底怎么选?附完整代码实现

2026/8/2 6:15:07

Spring AI 系列(三):RAG 开发必知——两种 Advisor 到底怎么选?附完整代码实现 Spring AI 提供了对常见 RAG 流程的开箱支持,通过 Advisor API 实现,向量数据库存储着AI模型所不知道的数据,当用…

技术博客内容聚合平台实践:OpenClaw 如何采集公开优质内容并生成每日技术内参

技术博客内容聚合平台实践:OpenClaw 如何采集公开优质内容并生成每日技术内参

2026/8/2 6:15:07

一、引言在信息爆炸的时代,技术从业者每天需要面对海量的技术博客、官方文档、行业资讯和开源项目动态。如果逐一打开各个博客站点、论坛和 Newsletter,不但耗时巨大,而且很难区分内容质量的高低,往往会陷入“每天阅读上百篇文章&…

SortMeRNA安装与实战:从环境配置到rRNA污染过滤全流程指南

SortMeRNA安装与实战:从环境配置到rRNA污染过滤全流程指南

2026/8/2 6:15:07

1. 项目概述:为什么SortMeRNA是rRNA污染过滤的“瑞士军刀”在宏基因组或转录组数据分析的流水线里,拿到原始测序数据后的第一步清洗工作,往往不是去除低质量碱基,而是剔除那些“不请自来”的核糖体RNA序列。这些rRNA序列&#xff…

搅拌设备机架各材质优缺点详解

搅拌设备机架各材质优缺点详解

2026/8/2 6:05:07

结合丰享搅拌设备落地工况,针对以上五种常用机架材质,逐一拆解真实优点、行业短板、适用边界,解决选型低配生锈、高配浪费、材质用错变形腐蚀等问题。1、普通喷漆碳钢 Q235-B优点:成本最低、焊接性能好、整体刚性稳定、不易变形、…

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案

2026/8/2 0:04:43

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾经从网易云音乐下载了心爱的歌曲&am…

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比

2026/8/2 0:04:43

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比工程导读:本文深入讨论 分布式配置中心选型实战:Nacos与Consul在创业场景下的对比 在生产工程实践中的核心落地方案。基于 分布式架构与微服务设计 视角,剖析实际痛点、架…

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案

2026/8/2 0:04:43

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案 【免费下载链接】MoneyPrinterPlus AI一键批量生成各类短视频,自动批量混剪短视频,自动把视频发布到抖音,快手,小红书,视频号上,赚钱从来没有这么容易过! 支持本地语音模型chatTTS,fasterwhisper,…

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案

2026/8/2 0:04:43

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾经从网易云音乐下载了心爱的歌曲&am…

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比

2026/8/2 0:04:43

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比工程导读:本文深入讨论 分布式配置中心选型实战:Nacos与Consul在创业场景下的对比 在生产工程实践中的核心落地方案。基于 分布式架构与微服务设计 视角,剖析实际痛点、架…

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案

2026/8/2 0:04:43

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案 【免费下载链接】MoneyPrinterPlus AI一键批量生成各类短视频,自动批量混剪短视频,自动把视频发布到抖音,快手,小红书,视频号上,赚钱从来没有这么容易过! 支持本地语音模型chatTTS,fasterwhisper,…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/1 0:03:03

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/2 5:08:03

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/2 1:50:52

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…