鸿蒙掌上驾考宝典应用开发40:设置页面——字体、深色模式、退出登录
第40篇:设置页面——字体、深色模式、退出登录

一、引言
设置页面是应用中最常用但也是最容易被低估的功能模块。它承载着用户偏好配置、账号安全管理、隐私协议查阅等关键功能。在 DriverLicenseExam 项目中,设置页面虽然看起来简洁,但其背后涉及的 CustomDialogController 弹窗管理、@Extend 装饰器扩展、原始资源文件读取等技术点值得深入学习。本文将结合源码深度解析设置页面的完整实现。
二、设置页面整体架构
2.1 页面结构
设置页面位于 products/entry/src/main/ets/pages/mine/Setting.ets,采用 NavDestination 作为页面容器,配合 Column 垂直布局组织菜单项:
// Setting.ets - 完整页面结构
@Entry
@ComponentV2
export struct SettingPage {
vm: CommonModel = CommonModel.instance;
logTag: string = 'AboutPage';
domainId: number = 0x0000;
build() {
NavDestination() {
Column({ space: 17 }) {
// 隐私协议
this.settingRow('隐私协议', () => {
const params: Record<string, 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)
function textExtend() {
.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 = new CustomDialogController({
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 弹窗内容构建
@Builder
logOutContent() {
Column() {
Row() {
Text('请确认是否退出当前账户?')
.fontSize(14);
}
.width('100%')
.borderRadius(16)
.margin({ left: '4%', right: '4%', bottom: 5 });
}
}
CustomContentDialog 是项目中封装的一个自定义弹窗组件,它提供了统一的弹窗样式,包括标题、内容和按钮组。这种封装方式使得弹窗在不同页面中保持视觉一致性。
3.3 退出登录的业务逻辑
// AccountUtil.loginOut
public 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 读取 rawfile:
getProtocolUrl(privacyUrl: string): string {
try {
// 从 AppScope/resources/rawfile 读取 data.json
const value: Uint8Array = (this.getUIContext().getHostContext() as Context)
.resourceManager.getRawFileContentSync('data.json');
return JSON.parse(buffer.from(value.buffer).toString())[privacyUrl] as string;
} 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/"
}
五、设置页面的完整交互流程
从用户点击"设置"到完成设置项操作的完整流程:
用户点击设置
│
▼
Navigation:pushPathByName('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 中添加新的 settingRow
this.settingRow('新增设置项', () => {
// 2. 设置点击处理逻辑
this.vm.navStack.pushPathByName('NewSettingPage', undefined);
});
// 3. 创建对应的设置页面
@Builder
export function NewSettingPageBuilder() {
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— 右侧箭头组件
更多推荐




所有评论(0)