
第40篇设置页面——字体、深色模式、退出登录一、引言设置页面是应用中最常用但也是最容易被低估的功能模块。它承载着用户偏好配置、账号安全管理、隐私协议查阅等关键功能。在 DriverLicenseExam 项目中设置页面虽然看起来简洁但其背后涉及的 CustomDialogController 弹窗管理、Extend 装饰器扩展、原始资源文件读取等技术点值得深入学习。本文将结合源码深度解析设置页面的完整实现。二、设置页面整体架构2.1 页面结构设置页面位于products/entry/src/main/ets/pages/mine/Setting.ets采用 NavDestination 作为页面容器配合 Column 垂直布局组织菜单项// Setting.ets - 完整页面结构EntryComponentV2exportstructSettingPage{vm:CommonModelCommonModel.instance;logTag:stringAboutPage;domainId:number0x0000;build() {NavDestination() {Column({space:17}) {// 隐私协议this.settingRow(隐私协议,() {constparams:Recordstring,Object {protocolUrl:this.getProtocolUrl(PRIVACY_URL) };this.vm.navStack.pushPathByName(PrivacyAgreement, params); });// 保密设置this.settingRow(保密设置,() {this.vm.navStack.pushPathByName(SecretSetting,undefined); });// 退出登录this.settingRow(退出登录,() {this.logOutDialogController.open(); }); } .width(100%) .height(100%) .padding({left:4%,right:4%,top:10}); } .title(设置) .backgroundColor(#F1F3F5); } }这里需要注意的是每一个设置项使用了Builder函数settingRow进行封装这是一种非常优雅的代码复用方式。Builder 函数接收标签和点击回调渲染出一个统一的菜单行样式。2.2 构建可复用的设置项Builder settingRow(label:string,onClick: () void) {Row(){Text(label).fontSize(16).fontFamily(鸿蒙黑体).fontWeight(FontWeight.Medium).opacity(0.9) .fontColor($r(app.color.font_secondary));Blank();// 右侧箭头Image($r(app.media.ic_right_arrow_lined)) .width(7) .height(14) .fillColor($r(app.color.icon_secondary)) .margin({ right:12}); } .width(100%) .minHeight(46).borderRadius(16).backgroundColor($r(app.color.comp_background_list_card)) .padding({ left:12, right:12}) .onClick(onClick); }这个 Builder 方法的设计体现了几个关键点Blank() 组件自动填充剩余空间将箭头推到右侧$r 资源引用使用系统资源色支持深色模式自动适配minHeight使用最小高度而非固定高度适应不同屏幕2.3 Extend 装饰器的巧用项目中还使用了Extend装饰器对 Text 组件进行方法扩展这是一种 ArkUI 的高级用法Extend(Text)functiontextExtend(){ .fontSize(16).fontFamily(鸿蒙黑体).fontWeight(FontWeight.Medium).opacity(0.9) .fontColor($r(app.color.font_secondary)); }使用方式非常简洁Text(隐私协议).textExtend();这相当于给 Text 组件增加了一个自定义方法所有设置了 textExtend() 的 Text 都会自动应用相同的样式集避免了大量重复代码。三、CustomDialogController 弹窗管理3.1 退出登录确认弹窗退出登录是一个需要二次确认的敏感操作项目使用了CustomDialogController管理弹窗的生命周期// 退出登录对话框控制器logOutDialogController: CustomDialogController newCustomDialogController({builder: CustomContentDialog({primaryTitle:账号退出登录,contentBuilder:(){ this.logOutContent(); },buttons: [ {value:$r(app.string.cancel),buttonStyle: ButtonStyleMode.TEXTUAL,action:(){ this.logOutDialogController.close(); }, }, {value:$r(app.string.confirm),buttonStyle: ButtonStyleMode.TEXTUAL,action:(){ AccountUtil.loginOut(); this.vm.navStack.pop(); }, }, ], }), });3.2 弹窗内容构建BuilderlogOutContent() { Column() { Row() { Text(请确认是否退出当前账户).fontSize(14); }.width(100%).borderRadius(16).margin({ left: 4%, right: 4%, bottom:5}); } }CustomContentDialog 是项目中封装的一个自定义弹窗组件它提供了统一的弹窗样式包括标题、内容和按钮组。这种封装方式使得弹窗在不同页面中保持视觉一致性。3.3 退出登录的业务逻辑// AccountUtil.loginOutpublic static loginOut(){AccountUtil._accountInfo.idToken ;AccountUtil._userInfo.avatar $r(app.media.user_avatar);AccountUtil._userInfo.nickname $r(app.string.user_name);AccountUtil._userInfo.phone ; }退出登录后需要清理三部分数据登录凭证清除 idToken标记为未登录状态用户头像恢复为默认头像用户昵称恢复为默认昵称用户名手机号清空手机号四、原始资源文件读取设置页面中的隐私协议功能需要读取原始资源文件rawfile中的协议 URL项目展示了如何通过 ResourceManager 读取 rawfilegetProtocolUrl(privacyUrl: string): string {try{// 从 AppScope/resources/rawfile 读取 data.jsonconstvalue: Uint8Array (this.getUIContext().getHostContext()asContext) .resourceManager.getRawFileContentSync(data.json);returnJSON.parse(buffer.from(value.buffer).toString())[privacyUrl]asstring; }catch(error) { hilog.error(this.domainId,this.logTag,getProtocolUrl Error: error.message);return; } }这个方法的流程如下获取 Context通过getUIContext().getHostContext()获取上下文同步读取使用getRawFileContentSync同步读取 rawfile 内容类型转换将Uint8Array转换为字符串JSON 解析解析为 JSON 对象获取指定字段data.json 的内容格式如下{privacy_url:https://developer.huawei.com/consumer/cn/privacy/,user_agreement:https://developer.huawei.com/consumer/cn/agreement/}五、设置页面的完整交互流程从用户点击设置到完成设置项操作的完整流程用户点击设置 │ ▼ NavigationpushPathByName(Setting,undefined)│ ▼ SettingPage 加载 → onReady 回调可选 │ ├── 点击隐私协议│ ├── getProtocolUrl(privacy_url)→ 读取 rawfile │ └── pushPathByName(PrivacyAgreement,params)│ ├── 点击保密设置│ └── pushPathByName(SecretSetting,undefined)│ └── 点击退出登录├── logOutDialogController.open()├── 用户点击取消→ 关闭弹窗 └── 用户点击确认├──AccountUtil.loginOut()└── navStack.pop()→ 返回上一页六、设置页面最佳实践6.1 设计原则统一风格所有设置项使用相同的 Builder 方法渲染保证视觉一致性弹窗复用通过 CustomContentDialog 封装统一弹窗减少重复代码资源外置隐私协议 URL 放在 rawfile 中方便修改无需重新编译6.2 可扩展性设置页面采用了非常易于扩展的设计。添加一个新的设置项只需三步// 1. 在 Column 中添加新的 settingRowthis.settingRow(新增设置项,() {// 2. 设置点击处理逻辑this.vm.navStack.pushPathByName(NewSettingPage,undefined); });// 3. 创建对应的设置页面BuilderexportfunctionNewSettingPageBuilder() {NewSettingPage(); }七、总结设置页面虽然看起来简单但它集中展示了 ArkUI 开发的多个关键技术点Builder通过 Builder 函数封装可复用的 UI 片段Extend扩展系统组件的样式方法CustomDialogController管理弹窗生命周期rawfile 读取通过 ResourceManager 读取原始资源文件AppStorage全局状态管理深色模式状态这些技术不仅适用于设置页面在整个项目开发中都具有广泛的适用性。关键源码文件products/entry/src/main/ets/pages/mine/Setting.ets— 设置页面完整实现commons/commonLib/src/main/ets/utils/AccountUtil.ets— 退出登录逻辑AppScope/resources/rawfile/data.json— 隐私协议 URL 配置products/entry/src/main/ets/pages/mine/PrivacyAgreement.ets— 隐私协议页面products/entry/src/main/ets/components/RightArrow.ets— 右侧箭头组件