鸿蒙 HarmonyOS 6 | Pura X Max 鸿蒙原生适配 19:设置页在 Pura X Max 上改成分组布局 原创
头像 小雨同学 2026-06-03 09:09:57    发布
4 浏览 0 点赞 0 收藏

前言

设置页很容易被写成一条长列表。账号、通知、权限、缓存、关于应用,全都从上到下排。手机外屏上这么写没有太大问题,用户打开设置页以后,从顶部一路往下找,看到需要的设置项就点进去。设置项数量不多时,单列列表甚至是最省事的写法。

我把同样的设置页放到 Pura X Max 展开态里看时,第一眼注意到的是设置项被拉得太长。左侧是设置标题,右侧是开关、状态或者跳转入口,中间被屏幕宽度拉开了一大段。用户看相机权限时,视线要从左侧标题扫到右侧状态;看版本号时,也要在一条很长的横向区域里找到对应值。单个设置项还能读,整页的分组关系却变弱了。

设置页和搜索页、列表页的使用方式不一样。搜索页会频繁调整条件,列表页主要用来浏览内容;设置页更像一个低频但要快速定位的功能入口集合。用户不一定每天打开设置页,但一旦打开,通常是带着明确目的进来的,比如改通知、看权限、清理缓存、查看版本、打开某个开关。这个时候,分类定位比单纯把列表拉宽更有价值。

这类设置页通常会包含几组内容。

  • 基础设置,比如显示方式、通知提醒、自动保存
  • 权限设置,比如相机、相册、麦克风、通知权限
  • 数据设置,比如缓存、同步、导入导出
  • 关于应用,比如版本号、隐私协议、用户协议、反馈入口

Pura X Max 展开态空间足够把设置页拆成左侧分类和右侧设置项,外屏和较窄窗口继续保留单列设置列表。鸿蒙里的全屏、分屏、自由窗口都会改变应用可用宽度,设置页如果只把手机端长列表拉宽,很快会遇到分类弱、行距远、定位慢这些问题。

我这次用一个设置页示例来验证这种改法。页面包含基础设置、权限设置、关于应用三个分类。小屏下仍然按分组从上到下展示;展开态下左侧显示分类卡片,右侧只展示当前分类下的设置项。这样用户在大屏里可以先选分类,再看具体设置,不需要在一条很长的设置列表里不断向下扫。

一、设置页不能只拉宽

1.1 外屏单列适合连续浏览

外屏下,设置页用单列结构是合理的。设置项按分组向下排列,用户进入页面以后,从顶部看到基础设置,再往下看到权限设置、关于应用。屏幕窄,单列列表能保证每个设置项都有足够的横向空间,标题、说明、右侧状态或开关都能放得下。

最常见的写法是这样的。

Column() {  this.SettingSection('基础设置')  this.SettingSection('权限设置')  this.SettingSection('关于应用')}.width('100%')

这个结构维护起来也简单。每个分组是一段列表,新增设置项时继续往对应分组里加。手机外屏上,用户滚动一下就能看到所有设置内容,页面也不会被左右拆开。

我以前做手机端设置页时,也会先用这种结构。设置页不是高频操作页,单列列表的可理解性很好。只要分组标题明确,设置项说明写得具体,小屏体验通常不会出大问题。这个时候没有必要为了大屏思维提前把页面拆复杂。

1.2 展开态单列会拉长信息路径

展开态里继续使用单列,问题会换一种方式出现。每一行设置项被拉得很宽,右侧开关或状态离左侧标题很远。用户看通知提醒时,标题在左,开关在右;看缓存清理时,说明在左,操作入口在右。单行看起来没有错,但整页的阅读路径被横向拉长了。

设置页在大屏里更需要分类定位。用户进来以后,应该先看到基础设置、权限设置、关于应用这些方向,再进入某一组具体设置。比如我想检查相机权限,就不该从通知提醒、自动保存、深色模式一路扫过去;我想看版本和隐私协议,也不需要穿过所有基础设置。

我会把大屏设置页拆成三个区域关系进行分析。


区域小屏处理展开态处理主要目的
设置分类作为分组标题嵌在列表中固定在左侧先定位方向
设置项列表所有分组连续排列只展示当前分类减少纵向寻找
状态与操作跟在每一行右侧保留在右侧列表内保持原有设置行为

这个表格能帮我避免一个误区。展开态不一定要展示更多设置项,它更应该把设置项的组织关系摆出来。左侧分类不是装饰,它承担的是入口定位;右侧设置项才是具体操作区域。

二、先拆分类,再拆设置项

2.1 分类要从标题变成入口

在单列设置页里,基础设置、权限设置、关于应用只是分组标题。它们帮助用户理解下面的设置项属于哪一类,但不会参与交互。到了展开态,这些分组标题可以升级成左侧分类导航。用户点击左侧分类,右侧切换对应设置项。

这个变化不只是把 UI 从上下结构改成左右结构,还牵涉到一个基础的状态设计。当前选中的设置分类要放在页面层,因为左侧分类和右侧设置项都要读取它。左侧需要知道哪个分类高亮,右侧需要知道展示哪组设置项。

示例里用 selectedGroupId 保存当前分类。

@State private selectedGroupId: number = 1;

这个状态不能放在左侧分类组件里。左侧分类只是触发切换的入口,右侧内容也需要它。把状态放在页面层以后,外屏和展开态都能读同一份当前分类状态,窗口宽度变化时也不会重置。

这里其实是一个很常见的编程常识:当两个区域都依赖同一个状态时,状态应该提升到它们共同的父级。设置页左右分栏只是一个具体场景,列表详情、搜索筛选、图片预览里的左右区域也会遇到同样的问题。

2.2 设置项要用同一套数据

我不建议为基础设置、权限设置、关于应用分别写三套 UI。它们的业务含义不同,但展示结构很接近:标题、说明、右侧内容、类型和状态。这样就可以抽成统一的设置项数据,再根据 type 决定右侧展示开关、文本、按钮还是跳转状态。

设置项可以先按这样的结构理解。


设置项类型页面表现示例
switch右侧显示开关通知提醒、自动保存
link右侧显示状态或入口相机权限、相册权限
value右侧显示文本值版本号、缓存大小
action右侧显示操作按钮清理缓存、导入示例

不同类型的设置项,可以走同一个 SettingRow(),再根据 type 决定右侧展示什么。这个写法比每个分组手写一堆 Row 更适合维护。后面新增数据同步、订阅管理、隐私协议、实验功能这些内容时,也只是新增数据,不需要改布局结构。

这里还要留意设置项说明的长度。说明文字太长,小屏里会把行高撑得过高,展开态里也会让右侧列表变得松散。设置项说明只解释当前设置的影响就够了,完整帮助文档不要放进设置行。

三、分栏前先保住右侧

3.1 分类栏不能抢设置项宽度

设置页的左侧分类栏不需要太宽,但右侧设置项列表必须能读。展开态分栏如果只看窗口是否超过某个阈值,可能会出现左侧分类栏出现了,右侧设置项却被压得很窄。标题、说明、开关、状态挤在一行里,页面看起来进入了大屏结构,实际操作并没有变轻松。

我会先给左侧分类栏和右侧设置项列表分别留宽度。示例里左侧分类栏是 260vp,右侧设置项列表至少保留 560vp,中间间距是 16vp。进入双栏前,先计算这些区域是否真的放得下。

private readonly groupPanelWidth: number = 260;private readonly detailMinWidth: number = 560;private readonly twoColumnGap: number = 16;

这些数字不用照搬。设置项比较短时,右侧 520vp 也可以;如果设置项说明更多,或者右侧同时出现开关、状态、按钮,右侧最小宽度就要提高。大屏适配不该看到宽度变大就马上分栏,分栏前要先确认主操作区域还能正常阅读和点击

3.2 可用宽度比屏幕宽度更有用

示例里的判断会先扣掉左右 padding,再计算左侧分类栏、右侧详情区和中间间距。

private canUseSplitSettings(): boolean {  const width = this.getEffectiveWidth();  const availableWidth = width - this.getPagePadding() * 2;  const requiredWidth = this.groupPanelWidth + this.twoColumnGap + this.detailMinWidth;​  return width >= this.expandedThreshold && availableWidth >= requiredWidth;}

这里有个常见误区,很多布局判断会直接拿窗口宽度和阈值比较。这个写法短期能用,但只要页面加了左右 padding、卡片间距、侧栏宽度,就容易在中间尺寸出问题。设置页这种页面尤其容易被忽略,因为它看起来只是普通列表,实际右侧每一行都有标题、说明、开关或状态,不能被压得太窄。

我会把 canUseSplitSettings() 放在页面层。页面层负责判断采用单列还是双栏;左侧分类组件只负责分类展示;右侧设置项列表只负责当前分类的设置项。这样组件职责会更清楚,后面新增设置分类时,也不会影响断点逻辑。

四、实际运行结果

4.1 外屏先保留单列列表

这个示例会模拟一个设置页。小屏下,基础设置、权限设置、关于应用三个分组按顺序排列,用户从上往下滚动。展开态下,左侧显示三个设置分类,右侧显示当前分类下的设置项。点击左侧分类时,右侧内容切换。

小屏状态下,我会继续保留单列列表。原因很直接:设置项虽然多,但屏幕宽度有限,强行做左右结构会让两边都很窄。用户在小屏里从上往下浏览,不会因为多一次滚动就失去方向。只要分组标题足够清楚,单列结构可以继续使用。

4.2 展开态再把分类放左侧

展开态截图要看左侧分类和右侧设置项之间的关系。左侧分类高亮当前分类,右侧只显示当前分类下的设置项。这样用户不需要在长列表里继续寻找权限、关于应用或者缓存设置。

这个示例里还保留了几个开关状态,比如通知提醒、自动保存、深色模式。真实项目里,这些状态可以来自本地持久化配置;权限类设置则要来自系统权限状态;关于应用里的版本号、协议入口、反馈入口可以来自应用配置。

五、真实项目时怎么处理

5.1 分类和设置项最好来自配置

示例里的设置组和设置项写在页面里,是为了让代码可以直接运行。真实项目里,设置项通常会随着版本持续增加,比如订阅、数据同步、隐私、缓存、实验功能。继续把所有设置行写死在页面里,后面会越来越难维护。

我会把设置页拆成配置数据:

  • 分类配置负责标题、说明、图标和排序
  • 设置项配置负责标题、说明、类型、右侧文案、点击动作
  • 页面状态负责当前选中分类和开关状态
  • 具体业务逻辑交给对应服务处理

这样设置页的 UI 结构会更稳定。新增一个设置项时,优先改配置;新增一个业务动作时,再补对应处理函数。页面本身不需要因为每个设置项都去加一段重复代码。

5.2 权限设置要接真实状态

权限设置是设置页里比较特殊的一类。示例里用去开启、已授权这类文案模拟状态,真实项目里要接系统权限查询结果。相机、相册、麦克风、通知这些权限,用户可能在系统设置里改掉,回到应用后页面要能刷新状态。

这个地方不能只靠本地开关模拟。权限状态应该来自系统能力或应用启动后的检查结果,设置页只负责展示和跳转。比如用户点击相机权限,页面可以跳到授权引导或系统权限设置;用户返回后,再刷新当前权限状态。

5.3 关于应用单独成组

关于应用这类信息经常被随手放到设置页底部。手机上这么放可以接受,展开态里如果继续和基础设置混在一起,用户会在通知开关、自动保存、缓存清理这些设置项之间找版本号和协议入口。

我会把关于应用单独做成一个分类。版本号、隐私政策、用户协议、反馈入口、开发者信息,都放到同一个分类下。这样右侧区域展示时也更清楚,用户不会在一条很长的设置列表里找这些低频但重要的入口。

总结

设置页在 Pura X Max 展开态里,不一定要继续做成长单列。外屏下,单列设置列表适合连续浏览;展开态里,把分类放到左侧、设置项放到右侧,用户更容易先定位方向,再处理具体设置。

我后面处理设置页时,会先把内容按这几类拆开:

  • 基础设置放常用开关,比如通知、自动保存、深色模式。
  • 权限设置放相机、相册、麦克风、通知权限,并接真实权限状态。
  • 关于应用放版本号、协议、反馈和开发者信息。
  • 数据和缓存类设置可以单独成组,不要混在基础设置里。
  • 展开态是否分栏,要先确认右侧设置项区域还有足够宽度。

真实项目里,设置页会随着版本持续增长。分类和设置项最好配置化,页面层只负责选中分类、布局判断和状态分发。这样外屏单列、展开态双栏都能读同一套数据,后面新增设置项时,也不会把页面写成越来越长的一整段 UI。

附:完整代码

interface SettingGroup {  id: number;  title: string;  desc: string;  badge: string;}​interface SettingItem {  id: number;  groupId: number;  title: string;  desc: string;  type: string;  value: string;  key: string;}​@Entry@Componentstruct Index {  // 页面真实宽度,由 onAreaChange 写入  @State private pageWidth: number = 0;​  // 演示宽度,只用于在同一个模拟器里观察外屏和展开态  @State private previewWidth: number = 0;​  // 展开态左侧选中的设置分类  @State private selectedGroupId: number = 1;​  // 模拟几个设置项状态,真实项目里可以来自持久化配置或系统权限查询  @State private notifyEnabled: boolean = true;  @State private autoSaveEnabled: boolean = true;  @State private darkModeEnabled: boolean = false;  @State private cacheClearedCount: number = 0;​  private readonly expandedThreshold: number = 860;  private readonly groupPanelWidth: number = 260;  private readonly detailMinWidth: number = 560;  private readonly twoColumnGap: number = 16;​  private readonly groups: SettingGroup[] = [    {      id: 1,      title: '基础设置',      desc: '通知、显示和保存偏好',      badge: '常用'    },    {      id: 2,      title: '权限设置',      desc: '相机、相册和麦克风权限',      badge: '权限'    },    {      id: 3,      title: '关于应用',      desc: '版本、协议和反馈入口',      badge: '信息'    }  ];​  private readonly items: SettingItem[] = [    {      id: 1,      groupId: 1,      title: '通知提醒',      desc: '处理结果保存后,按提醒时间发送通知',      type: 'switch',      value: '',      key: 'notify'    },    {      id: 2,      groupId: 1,      title: '自动保存',      desc: '识别结果确认后自动保存到本地记录',      type: 'switch',      value: '',      key: 'autoSave'    },    {      id: 3,      groupId: 1,      title: '深色模式',      desc: '跟随系统外观,夜间查看内容时减少刺眼背景',      type: 'switch',      value: '',      key: 'darkMode'    },    {      id: 4,      groupId: 1,      title: '清理缓存',      desc: '清理临时缩略图和识别过程缓存',      type: 'action',      value: '清理',      key: 'cache'    },    {      id: 5,      groupId: 2,      title: '相机权限',      desc: '用于拍摄通知、票据和白板照片',      type: 'link',      value: '去开启',      key: 'camera'    },    {      id: 6,      groupId: 2,      title: '相册权限',      desc: '用于从相册选择已有图片进行整理',      type: 'link',      value: '已授权',      key: 'album'    },    {      id: 7,      groupId: 2,      title: '麦克风权限',      desc: '用于后续语音整理和会议内容识别',      type: 'link',      value: '去开启',      key: 'microphone'    },    {      id: 8,      groupId: 2,      title: '通知权限',      desc: '用于发送待办提醒和处理结果提醒',      type: 'link',      value: '已授权',      key: 'push'    },    {      id: 9,      groupId: 3,      title: '当前版本',      desc: '查看当前安装的应用版本',      type: 'value',      value: '1.0.0',      key: 'version'    },    {      id: 10,      groupId: 3,      title: '隐私政策',      desc: '查看数据存储、权限使用和第三方服务说明',      type: 'link',      value: '查看',      key: 'privacy'    },    {      id: 11,      groupId: 3,      title: '用户协议',      desc: '查看应用使用条款和免责声明',      type: 'link',      value: '查看',      key: 'terms'    },    {      id: 12,      groupId: 3,      title: '问题反馈',      desc: '提交使用过程中遇到的问题或建议',      type: 'link',      value: '反馈',      key: 'feedback'    }  ];​  // Demo 中优先使用演示宽度,真实项目里可以直接返回 pageWidth  private getEffectiveWidth(): number {    if (this.previewWidth > 0) {      return this.previewWidth;    }​    return this.pageWidth;  }​  private getPagePadding(): number {    if (this.getEffectiveWidth() >= this.expandedThreshold) {      return 24;    }​    return 16;  }​  // 分栏前先确认左侧分类栏、间距和右侧设置项区域都能放下  private canUseSplitSettings(): boolean {    const width = this.getEffectiveWidth();    const availableWidth = width - this.getPagePadding() * 2;    const requiredWidth = this.groupPanelWidth + this.twoColumnGap + this.detailMinWidth;​    return width >= this.expandedThreshold && availableWidth >= requiredWidth;  }​  private isExpanded(): boolean {    return this.canUseSplitSettings();  }​  private getContentWidth(): Length {    if (this.previewWidth > 0) {      return this.previewWidth;    }​    return '100%';  }​  private getTitleSize(): number {    return this.isExpanded() ? 28 : 23;  }​  private getModeText(): string {    return this.isExpanded() ? 'expanded · 分类 + 设置项' : 'compact · 单列设置';  }​  private getModeDesc(): string {    if (this.isExpanded()) {      return '展开态下左侧显示设置分类,右侧显示当前分类下的设置项。';    }​    return '小屏下设置项按分组从上到下排列,保持普通设置列表结构。';  }​  private setPreview(width: number) {    this.previewWidth = width;  }​  private getSelectedGroup(): SettingGroup {    const found = this.groups.find((item: SettingGroup) => item.id === this.selectedGroupId);    return found ? found : this.groups[0];  }​  private getItemsByGroup(groupId: number): SettingItem[] {    return this.items.filter((item: SettingItem) => item.groupId === groupId);  }​  private isSwitchOn(key: string): boolean {    if (key === 'notify') {      return this.notifyEnabled;    }​    if (key === 'autoSave') {      return this.autoSaveEnabled;    }​    if (key === 'darkMode') {      return this.darkModeEnabled;    }​    return false;  }​  private toggleSwitch(key: string) {    if (key === 'notify') {      this.notifyEnabled = !this.notifyEnabled;    } else if (key === 'autoSave') {      this.autoSaveEnabled = !this.autoSaveEnabled;    } else if (key === 'darkMode') {      this.darkModeEnabled = !this.darkModeEnabled;    }  }​  private handleAction(key: string) {    if (key === 'cache') {      this.cacheClearedCount += 1;    }  }​  @Builder  private PreviewButton(text: string, width: number) {    Text(text)      .fontSize(12)      .fontColor(this.previewWidth === width ? '#FFFFFF' : '#2F8F83')      .textAlign(TextAlign.Center)      .padding({ left: 10, right: 10, top: 7, bottom: 7 })      .backgroundColor(this.previewWidth === width ? '#2F8F83' : '#E6F4F1')      .borderRadius(999)      .onClick(() => {        this.setPreview(width);      })  }​  @Builder  private HeaderPanel() {    Column({ space: 10 }) {      Row({ space: 10 }) {        Column({ space: 4 }) {          Text('设置页在 Pura X Max 上改成分组布局')            .fontSize(this.getTitleSize())            .fontWeight(FontWeight.Bold)            .fontColor('#111827')            .maxLines(1)            .textOverflow({ overflow: TextOverflow.Ellipsis })​          Text(this.getModeText())            .fontSize(14)            .fontColor('#2F8F83')            .maxLines(1)            .textOverflow({ overflow: TextOverflow.Ellipsis })        }        .layoutWeight(1)​        Text('窗口 ' + Math.round(this.pageWidth).toString() + 'vp')          .fontSize(12)          .fontColor('#374151')          .padding({ left: 10, right: 10, top: 6, bottom: 6 })          .backgroundColor('#FFFFFF')          .borderRadius(999)      }      .width('100%')​      Text('演示宽度:' + Math.round(this.getEffectiveWidth()).toString() + 'vp。' + this.getModeDesc())        .fontSize(14)        .fontColor('#6B7280')        .lineHeight(21)        .maxLines(2)        .textOverflow({ overflow: TextOverflow.Ellipsis })​      Row({ space: 8 }) {        this.PreviewButton('自动', 0)        this.PreviewButton('外屏', 430)        this.PreviewButton('展开态', 1040)      }      .width('100%')    }    .width('100%')  }​  @Builder  private GroupBadge(text: string, selected: boolean) {    Text(text)      .fontSize(11)      .fontColor(selected ? '#FFFFFF' : '#2F8F83')      .padding({ left: 7, right: 7, top: 3, bottom: 3 })      .backgroundColor(selected ? '#33FFFFFF' : '#E6F4F1')      .borderRadius(999)  }​  @Builder  private GroupCard(item: SettingGroup) {    Column({ space: 8 }) {      Row() {        Text(item.title)          .fontSize(16)          .fontWeight(FontWeight.Medium)          .fontColor(this.selectedGroupId === item.id ? '#FFFFFF' : '#111827')          .maxLines(1)          .textOverflow({ overflow: TextOverflow.Ellipsis })​        Blank()​        this.GroupBadge(item.badge, this.selectedGroupId === item.id)      }      .width('100%')​      Text(item.desc)        .fontSize(13)        .fontColor(this.selectedGroupId === item.id ? '#DFF5F1' : '#6B7280')        .lineHeight(19)        .maxLines(2)        .textOverflow({ overflow: TextOverflow.Ellipsis })    }    .width('100%')    .padding(14)    .backgroundColor(this.selectedGroupId === item.id ? '#2F8F83' : '#FFFFFF')    .borderRadius(20)    .border({      width: 1,      color: this.selectedGroupId === item.id ? '#2F8F83' : '#E5E7EB'    })    .onClick(() => {      this.selectedGroupId = item.id;    })  }​  @Builder  private GroupPanel() {    Column({ space: 14 }) {      Column({ space: 4 }) {        Text('设置分类')          .fontSize(20)          .fontWeight(FontWeight.Bold)          .fontColor('#111827')​        Text('先选分类,再看对应设置项')          .fontSize(13)          .fontColor('#6B7280')      }      .width('100%')​      ForEach(this.groups, (item: SettingGroup) => {        this.GroupCard(item)      }, (item: SettingGroup) => item.id.toString())    }    .width('100%')    .height('100%')    .padding(16)    .backgroundColor('#FFFFFF')    .borderRadius(26)    .shadow({      radius: 12,      color: '#10000000',      offsetX: 0,      offsetY: 4    })  }​  @Builder  private SwitchView(key: string) {    Row() {      if (this.isSwitchOn(key)) {        Blank()​        Circle()          .width(22)          .height(22)          .fill('#FFFFFF')          .margin({ right: 3 })      } else {        Circle()          .width(22)          .height(22)          .fill('#FFFFFF')          .margin({ left: 3 })​        Blank()      }    }    .width(48)    .height(28)    .backgroundColor(this.isSwitchOn(key) ? '#2F8F83' : '#CBD5E1')    .borderRadius(14)    .onClick(() => {      this.toggleSwitch(key);    })  }​  @Builder  private RightContent(item: SettingItem) {    if (item.type === 'switch') {      this.SwitchView(item.key)    } else if (item.type === 'action') {      Text(item.value)        .fontSize(13)        .fontColor('#2F8F83')        .padding({ left: 10, right: 10, top: 6, bottom: 6 })        .backgroundColor('#E6F4F1')        .borderRadius(999)        .onClick(() => {          this.handleAction(item.key);        })    } else {      Text(item.value)        .fontSize(13)        .fontColor(item.type === 'value' ? '#6B7280' : '#2F8F83')        .maxLines(1)        .textOverflow({ overflow: TextOverflow.Ellipsis })    }  }​  @Builder  private SettingRow(item: SettingItem) {    Row({ space: 12 }) {      Column({ space: 4 }) {        Text(item.title)          .fontSize(16)          .fontWeight(FontWeight.Medium)          .fontColor('#111827')          .maxLines(1)          .textOverflow({ overflow: TextOverflow.Ellipsis })​        Text(item.desc)          .fontSize(13)          .fontColor('#6B7280')          .lineHeight(20)          .maxLines(this.isExpanded() ? 2 : 3)          .textOverflow({ overflow: TextOverflow.Ellipsis })      }      .layoutWeight(1)​      this.RightContent(item)    }    .width('100%')    .padding(15)    .backgroundColor('#FFFFFF')    .borderRadius(20)    .border({      width: 1,      color: '#E5E7EB'    })  }​  @Builder  private SettingSection(group: SettingGroup) {    Column({ space: 12 }) {      Row() {        Column({ space: 4 }) {          Text(group.title)            .fontSize(18)            .fontWeight(FontWeight.Bold)            .fontColor('#111827')​          Text(group.desc)            .fontSize(13)            .fontColor('#6B7280')        }        .layoutWeight(1)​        this.GroupBadge(group.badge, false)      }      .width('100%')      .padding({ left: 4, right: 4 })​      ForEach(this.getItemsByGroup(group.id), (item: SettingItem) => {        this.SettingRow(item)      }, (item: SettingItem) => item.id.toString())    }    .width('100%')  }​  @Builder  private DetailPanel() {    Column({ space: 14 }) {      Row() {        Column({ space: 4 }) {          Text(this.getSelectedGroup().title)            .fontSize(22)            .fontWeight(FontWeight.Bold)            .fontColor('#111827')            .maxLines(1)            .textOverflow({ overflow: TextOverflow.Ellipsis })​          Text(this.getSelectedGroup().desc)            .fontSize(13)            .fontColor('#6B7280')            .maxLines(1)            .textOverflow({ overflow: TextOverflow.Ellipsis })        }        .layoutWeight(1)​        if (this.cacheClearedCount > 0) {          Text('清理 ' + this.cacheClearedCount.toString() + ' 次')            .fontSize(12)            .fontColor('#6B7280')        }      }      .width('100%')      .padding({ left: 4, right: 4 })​      Scroll() {        Column({ space: 12 }) {          ForEach(this.getItemsByGroup(this.selectedGroupId), (item: SettingItem) => {            this.SettingRow(item)          }, (item: SettingItem) => item.id.toString())        }        .width('100%')        .padding({ bottom: 24 })      }      .layoutWeight(1)      .width('100%')      .edgeEffect(EdgeEffect.Spring)    }    .width('100%')    .height('100%')    .padding(18)    .backgroundColor('#FFFFFF')    .borderRadius(26)    .shadow({      radius: 12,      color: '#10000000',      offsetX: 0,      offsetY: 4    })  }​  @Builder  private CompactSettingsList() {    Scroll() {      Column({ space: 22 }) {        ForEach(this.groups, (group: SettingGroup) => {          this.SettingSection(group)        }, (group: SettingGroup) => group.id.toString())      }      .width('100%')      .padding({ bottom: 24 })    }    .layoutWeight(1)    .width('100%')    .edgeEffect(EdgeEffect.Spring)  }​  @Builder  private MainContent() {    if (this.isExpanded()) {      Row({ space: this.twoColumnGap }) {        Column() {          this.GroupPanel()        }        .width(this.groupPanelWidth)        .height('100%')        .flexShrink(0)​        Column() {          this.DetailPanel()        }        .layoutWeight(1)        .height('100%')      }      .width('100%')      .height('100%')    } else {      this.CompactSettingsList()    }  }​  build() {    Column() {      Column({ space: 16 }) {        this.HeaderPanel()        this.MainContent()      }      .width(this.getContentWidth())      .height('100%')      .padding({        left: this.getPagePadding(),        right: this.getPagePadding(),        top: 18,        bottom: 16      })    }    .width('100%')    .height('100%')    .alignItems(HorizontalAlign.Center)    .backgroundColor('#F6F7F9')    .onAreaChange((_: Area, newValue: Area) => {      const width = Number(newValue.width);      if (!Number.isNaN(width) && width > 0) {        this.pageWidth = width;      }    })  }}


©本站发布的所有内容,包括但不限于文字、图片、音频、视频、图表、标志、标识、广告、商标、商号、域名、软件、程序等,除特别标明外,均来源于网络或用户投稿,版权归原作者或原出处所有。我们致力于保护原作者版权,若涉及版权问题,请及时联系我们进行处理。
分类
HarmonyOS

暂无评论数据

加载中...

发布

头像

小雨同学

产品总监、独立开发者社群主理人、资深全栈工程师,HarmonyOS应用开发者高级认证,PMP认证,CSDN博客专家,鸿蒙极客,Trae Fellow,阿里云社区专家博主、51CTO 博客专家、OpenTiny 优秀布道师、科大讯飞荣誉讲师。

19

帖子

0

提问

145

粉丝

关注
热门推荐
地址:北京市朝阳区北三环东路三元桥曙光西里甲1号第三置业A座1508室 商务内容合作QQ:2291221 电话:13391790444或(010)62178877
版权所有:电脑商情信息服务集团 北京赢邦策略咨询有限责任公司
声明:本媒体部分图片、文章来源于网络,版权归原作者所有,我司致力于保护作者版权,如有侵权,请与我司联系删除

京ICP备:2022009079号-2

京公网安备:11010502051901号

ICP证:京B2-20230255