news 2026/7/27 10:26:50

HarmonyOS 应用开发《掌上英语》第54篇:设置页面全解析——从主题选择到数据清除

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS 应用开发《掌上英语》第54篇:设置页面全解析——从主题选择到数据清除

设置页面全解析——从主题选择到数据清除

引言

设置页面是每个应用都不可或缺的"控制中心",它汇聚了用户偏好配置、账户管理、应用信息等关键功能。一个设计良好的设置页面应当做到:功能分类清晰、交互反馈即时、配置项可持久化。本文将以SetUpPage为例,深入解析其在 HarmonyOS 上的完整实现。

SetUpPage位于features/minePage模块的views/SetupPage.ets中,是"我的"模块中的核心页面之一。它通过@Builder函数SetUpPageBuilder导出,注册在路由表中供全局导航使用。

设置项的模型设计

每个设置项对应一个SetupItem模型:

@ObservedV2exportclassSetupItem{@Tracemsg:string// 设置项标题@Traceicon:ResourceStr// 右侧图标@Tracecache?:string// 缓存大小(仅"清除缓存"项使用)@Traceurl?:string// 点击跳转的目标路由}

SetupItem使用@ObservedV2@Trace装饰器,保证属性的变化(如缓存大小的动态更新)能自动触发 UI 重绘。cache字段为可选字段,只有缓存相关的设置项才会使用;url字段绑定路由标识,使得设置项的点击行为可以通过数据驱动而非硬编码。

设置项列表的实现

列表数据初始化

设置项列表在组件内部直接定义:

@LocalsetupList:SetupItem[]=[newSetupItem('个人信息',$r('app.media.iconRight'),'',RouterMap.PERSONAL_CENTER_PAGE),newSetupItem('夜间模式',$r('app.media.iconRight'),'',''),newSetupItem('学习计划',$r('app.media.iconRight'),'','LearningPlanSettingPage'),newSetupItem('学习提醒',$r('app.media.iconRight'),'','ReminderSettingPage'),newSetupItem('隐私协议',$r('app.media.iconRight'),'','PrivacyStatementPage'),newSetupItem('关于',$r('app.media.iconRight'),'',RouterMap.ABOUT_PAGE),newSetupItem('清除缓存',$r('app.media.iconRight'),'0.0M',''),];

这里涉及的路由标识既有RouterMap枚举值(如RouterMap.PERSONAL_CENTER_PAGE),也有直接字符串(如'LearningPlanSettingPage')。使用RouterMap枚举的好处是编译期类型检查,避免字符串拼写错误;而直接使用字符串则更为灵活,适用于尚未纳入枚举的路由。

UI 渲染

每个设置项由SetupComponent组件渲染:

struct SetupComponent{@Param@RequiresetupItem:SetupItem;@Param@RequireselectItem:string='跟随系统';build(){Stack(){Row(){Text(this.setupItem.msg).fontColor($r('sys.color.font_primary')).fontSize($r('sys.float.Body_L'));Row(){if(this.setupItem.cache){Text(this.setupItem.cache)// 显示缓存大小.fontColor($r('sys.color.font_primary')).fontWeight(FontWeight.Medium)}Image(this.setupItem.icon)// 右侧箭头图标}}.width('100%').backgroundColor($r('sys.color.comp_background_list_card')).borderRadius($r('app.float.vp_16')).justifyContent(FlexAlign.SpaceBetween)}}}

组件采用左右布局:左侧是设置项标题文本,右侧是缓存数值(如有)加箭头图标。justifyContent(FlexAlign.SpaceBetween)保证左右内容自动撑开两端对齐。

点击事件分发

外层通过ForEach循环为每个设置项绑定点击事件:

ForEach(this.setupList,(item:SetupItem)=>{SetupComponent({...}).onClick(()=>{if(item.msg==='清除缓存'){this.showAlert(AlertType.clean);}elseif(item.msg==='夜间模式'){this.darkDialog.open();}elseif(item.msg==='个人信息'){if(item.url&&this.logoUser.isLogin){RouterModule.push({url:item.url});}else{RouterModule.push({url:RouterMap.LOGIN_PAGE,param:false});}}else{if(item.url){RouterModule.push({url:item.url});}}})})

事件分发策略是:特殊项(清除缓存、夜间模式)弹出对话框,个人信息项需要检查登录状态,其余常规项直接通过RouterModule.push导航。这种"数据驱动 + 特殊判断"的混合模式,在保持代码简洁的同时也覆盖了页面特有的交互逻辑。

夜间模式切换

夜间模式是设置页面的核心功能之一,实现涉及三个层面:UI 交互、系统 API 调用和偏好持久化。

选择弹窗

夜间模式的设置通过DarkColorDialog自定义弹窗实现:

@CustomDialogexportstruct DarkColorDialog{selectItem:string='';selectItemBack:(selectItem:string)=>void=()=>{};cancel:()=>void=()=>{};}

弹窗内使用 Grid 布局展示三个选项:跟随系统普通模式夜间模式。每个选项左侧显示文字,右侧显示选中标记:

Row(){Text(item.label)Image(item.label===this.selectItem?$r('app.media.icon_Checked'):$r('app.media.icon_ans_common'))}

当前选中的选项显示勾选图标(icon_Checked),未选中的显示普通圆形图标(icon_ans_common),视觉对比清晰。

模式切换逻辑

当用户选择某个模式后,SetUpPagehandleDark方法被调用:

handleDark(item:string){letcolorMode:number=1;if(item==='跟随系统'){colorMode=-1;this.getUIContext()?.getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);}elseif(item==='普通模式'){colorMode=1;this.getUIContext()?.getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);}else{colorMode=0;this.getUIContext()?.getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);}PreferenceUtil.getInstance().put(PreferConstant.COLOR_MODE,colorMode);}

核心是调用ApplicationContext.setColorMode()方法设置主题模式,该方法接受ConfigurationConstant.ColorMode枚举值:

  • COLOR_MODE_NOT_SET(-1):跟随系统
  • COLOR_MODE_LIGHT(1):浅色模式
  • COLOR_MODE_DARK(0):深色模式

设置完成后,通过PreferenceUtil将选择持久化到首选项文件中。下次应用启动时,aboutToAppear会读取该值并恢复:

aboutToAppear():void{letcolorMode=PreferenceUtil.getInstance().get(PreferConstant.COLOR_MODE,-1)asnumber;this.selectItem=this.getColorItem(colorMode);}

getColorItem方法将数字映射为对应的描述字符串,完成从存储到展示的闭环。

缓存清除

缓存清除是典型的"风险操作",需要谨慎确认后再执行。SetUpPage通过两步确认机制实现:先弹框确认,再执行清除。

确认弹框

使用系统级showAlertDialogAPI:

showAlert(type:AlertType){this.getUIContext().showAlertDialog({message:type===AlertType.clean?'请确认是否清除缓存':'确定要退出登录吗',autoCancel:false,buttons:[{value:'取消',action:()=>{}},{value:'确定',action:()=>{if(type===AlertType.clean)this.clean();elsethis.loginOut();}}]});}

AlertType枚举区分"清除缓存"和"退出登录"两种弹框类型,共享同一个弹框方法但展示不同的文案和执行逻辑。

缓存大小读取

在页面初始化时,通过storageStatistics.getCurrentBundleStats获取应用的存储统计数据:

getClearSize(){storageStatistics.getCurrentBundleStats((error,bundleStats)=>{this.setupList.forEach(eme=>{if(eme.msg==='清除缓存'){letisClean=PreferenceUtil.getInstance().isClean()&&PreferenceUtil.getInstance(PreferConstant.TOPIC_NOTES).isClean();if(isClean){eme.cache='0KB';}else{eme.cache=(bundleStats.dataSize/1024).toFixed(2)+'KB';}}});});}

这里有一个巧妙的设计:通过PreferenceUtilisClean()方法判断是否已经执行过清除。如果两个首选项实例均为初始状态(未写入过任何数据),则直接显示0KB,否则从bundleStats.dataSize计算真实缓存大小。

执行清除

clean方法的清除策略涉及多个目录:

asyncclean(){letpaths:Array<string>=[];letcontext=this.getUIContext()?.getHostContext()!;letmoduleContext=awaitapplication.createModuleContext(context,'entry');// 收集需要清除的目录paths.push(moduleContext.cacheDir);paths.push(context.cacheDir);paths.push(moduleContext.preferencesDir);paths.push(context.preferencesDir);// EL1 加密区域的 cache 和 preferences 目录moduleContext.area=contextConstant.AreaMode.EL1;context.area=contextConstant.AreaMode.EL1;paths.push(moduleContext.cacheDir);paths.push(context.cacheDir);paths.push(moduleContext.preferencesDir);paths.push(context.preferencesDir);// 清除所有 PreferenceUtil 的数据PreferenceUtil.getInstance().clear();PreferenceUtil.getInstance(PreferConstant.TOPIC_NOTES).clear();// 逐文件删除for(letpathofpaths){letfilenames=awaitfileIo.listFile(path);for(letfilenameoffilenames){letdirPath=path+'/'+filename;letisDirectory=fileIo.statSync(dirPath).isDirectory();if(isDirectory){fileIo.unlinkSync(dirPath);// 目录用同步删除}else{fileIo.unlink(dirPath);// 文件用异步删除}}}this.getClearSize();// 重新计算缓存大小}

清除范围覆盖了主工程模块和 entry 模块的cacheDirpreferencesDir,同时针对 EL1(加密等级 1)区域也进行了清除。对于目录使用unlinkSync同步删除,对于文件使用unlink异步删除——这种差异化处理既避免了异步删除目录可能引发的并发问题,又利用异步删除提升文件删除效率。最后重新调用getClearSize()刷新 UI 上的缓存数值。

隐私协议与关于页面

对于隐私协议这类静态展示页面,SetUpPage通过路由跳转的方式将它们分发到各自的独立页面。路由表中注册了多个相关页面:

  • PrivacyStatementPage:隐私声明
  • PrivacyAgreementBuilder:隐私协议
  • PrivacyUseAlertBuilder:隐私使用说明
  • AboutPage:关于页面(展示应用名称、版本号、图标等信息)
  • FeedbackSubmitPage:意见反馈页面

这些页面通过RouterMap统一管理,在设置项中按需引用。

退出登录

退出登录位于设置列表底部,作为一个独立的大按钮展示。点击后同样通过showAlertDialog确认,确认后将UserInfo.isLogin置为 false,弹出 Toast 提示"退出成功",然后调用RouterModule.pop()返回上一页。

总结

SetUpPage是一个功能完整、设计成熟的设置页面实现。其技术亮点可以归纳为:

  1. 数据驱动:设置项列表通过SetupItem模型定义,增删改设置项只需修改数据数组,无需改动 UI 代码。
  2. 主题切换:利用ApplicationContext.setColorMode()系统 API 实现夜间模式,搭配DarkColorDialog提供友好的交互选择界面。
  3. 缓存管理:通过storageStatistics读取缓存大小,使用fileIo实现多目录递归删除,覆盖 EL1 加密区域。
  4. 安全机制:清除缓存和退出登录都通过 AlertDialog 二次确认,避免误操作。
  5. 状态持久化:主题选择通过PreferenceUtil持久化存储,应用重启后自动恢复用户偏好。

对于需要构建设置页面的 HarmonyOS 应用,SetUpPage提供了一份可参考的完整实现方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/27 10:25:59

鸣潮自动化工具ok-ww终极指南:3步安装+智能后台运行完全教程

鸣潮自动化工具ok-ww终极指南&#xff1a;3步安装智能后台运行完全教程 【免费下载链接】ok-wuthering-waves 鸣潮 后台自动战斗 自动刷声骸 一键日常 Automation for Wuthering Waves 项目地址: https://gitcode.com/GitHub_Trending/ok/ok-wuthering-waves ok-ww是一款…

作者头像 李华
网站建设 2026/7/27 10:25:24

GWO-BP-AdaBoost集成模型在电力预测中的应用

1. 项目概述 在机器学习领域&#xff0c;预测模型的精度和泛化能力一直是研究者关注的核心问题。传统单一算法往往难以兼顾全局搜索能力和局部优化效果&#xff0c;而集成学习通过组合多个弱学习器可以显著提升模型性能。本文将详细介绍一种创新的预测模型——GWO-BP-AdaBoost&…

作者头像 李华
网站建设 2026/7/27 10:24:40

Intellij Idea+Java+Maven项目报错处理合集(不定期更新)

Intellij Idea&#xff1a;2025.2.6.2(community edition) Java&#xff1a;17/21 Maven&#xff1a;3.9.15 一、导入(import)报错&#xff1a; 第一种方法&#xff1a; 第二种方法&#xff1a; 二、拉取(pull)/提交(commit)报错&#xff1a; 第一种方法&#xff1a; 接着…

作者头像 李华
网站建设 2026/7/27 10:23:06

深入解析DP83815单芯片以太网控制器:架构、驱动与实战

1. 项目概述&#xff1a;深入理解单芯片集成以太网控制器在嵌入式系统、工业控制、传统PC主板乃至一些特定网络设备的设计中&#xff0c;我们常常会遇到一个核心需求&#xff1a;如何以最低的成本、最简化的外围电路&#xff0c;实现一个稳定可靠的10/100兆以太网连接。十几年前…

作者头像 李华
网站建设 2026/7/27 10:16:09

FREE!ship Plus:5分钟快速上手开源船舶设计软件的完整指南

FREE!ship Plus&#xff1a;5分钟快速上手开源船舶设计软件的完整指南 【免费下载链接】freeship-plus-in-lazarus FreeShip Plus in Lazarus 项目地址: https://gitcode.com/gh_mirrors/fr/freeship-plus-in-lazarus FREE!ship Plus是一款基于Lazarus/Free Pascal开发的…

作者头像 李华