项目已开源,开源地址: gitcode.com/nutpi/Harmo... , 欢迎fork & star
效果演示

1. 概述
在上一篇教程中,我们学习了如何使用HarmonyOS NEXT的GridRow和GridCol组件实现基础的设置选项列表网格布局。本篇教程将在此基础上,深入探讨如何优化和扩展设置选项列表,实现更加灵活、美观和功能丰富的界面。
本教程将涵盖以下内容:
- 设置选项分组
- 响应式布局设计
- 设置选项的交互设计
- 动画效果实现
- 主题与样式定制
- GridRow和GridCol的高级配置
2. 设置选项分组
2.1 分组数据结构设计
首先,我们需要扩展数据结构,支持设置选项的分组:
typescript
interface SettingsType {
title: string;
icon: Resource;
description?: string; // 可选的描述文本
hasSwitch?: boolean; // 是否显示开关
isOn?: boolean; // 开关状态
}
interface SettingsGroupType {
groupTitle: string; // 分组标题
items: SettingsType[]; // 分组内的设置项
}
这个扩展的数据结构增加了以下特性:
- 为设置项添加了可选的描述文本
- 为设置项添加了开关控制选项
- 创建了分组结构,每个分组包含一个标题和多个设置项
2.2 分组数据准备
typescript
private settingsGroups: SettingsGroupType[] = [
{
groupTitle: '账号',
items: [
{ title: '账号与安全', icon: $r('app.media.01'), description: '管理您的账号信息和安全设置' },
{ title: '隐私设置', icon: $r('app.media.03'), description: '管理您的隐私和数据' }
]
},
{
groupTitle: '通知与声音',
items: [
{ title: '通知设置', icon: $r('app.media.02'), hasSwitch: true, isOn: true },
{ title: '声音设置', icon: $r('app.media.04'), hasSwitch: true, isOn: true }
]
},
{
groupTitle: '通用',
items: [
{ title: '通用设置', icon: $r('app.media.04') },
{ title: '语言', icon: $r('app.media.05'), description: '简体中文' },
{ title: '深色模式', icon: $r('app.media.01'), hasSwitch: true, isOn: false }
]
},
{
groupTitle: '关于',
items: [
{ title: '帮助与反馈', icon: $r('app.media.05') },
{ title: '关于我们', icon: $r('app.media.01'), description: '版本 1.0.0' }
]
}
]
2.3 分组布局实现
typescript
build() {
Column() {
Text('设置')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
.width('100%')
.textAlign(TextAlign.Start)
ForEach(this.settingsGroups, (group: SettingsGroupType, groupIndex: number) => {
// 分组标题
Text(group.groupTitle)
.fontSize(14)
.fontColor('#666666')
.margin({ top: groupIndex > 0 ? 24 : 0, bottom: 8, left: 8 })
.width('100%')
.textAlign(TextAlign.Start)
// 分组内的设置项
GridRow({ columns: 1 }) {
ForEach(group.items, (setting: SettingsType) => {
GridCol({ span: 1 }) {
this.SettingItem(setting)
}
})
}
})
}
.width('100%')
.padding(16)
}
2.4 设置项Builder实现
typescript
@Builder
private SettingItem(setting: SettingsType) {
Row() {
Image(setting.icon)
.width(24)
.height(24)
.margin({ right: 16 })
Column() {
Text(setting.title)
.fontSize(16)
if (setting.description) {
Text(setting.description)
.fontSize(12)
.fontColor('#999999')
.margin({ top: 4 })
}
}
.alignItems(HorizontalAlign.Start)
Blank()
if (setting.hasSwitch) {
Toggle({ type: ToggleType.Switch, isOn: setting.isOn ?? false })
.margin({ left: 8 })
.onChange((isOn: boolean) => {
setting.isOn = isOn
})
} else {
Image($r("app.media.arrowright"))
.width(16)
.height(16)
}
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.margin({ bottom: 8 })
}
3. 响应式布局设计
3.1 断点配置
为了使设置选项列表能够适应不同屏幕尺寸的设备,我们需要配置断点和响应式列数:
typescript
// 在build方法中
Column() {
// 标题...
ForEach(this.settingsGroups, (group: SettingsGroupType, groupIndex: number) => {
// 分组标题...
// 分组内的设置项
GridRow({
columns: { xs: 1, sm: 1, md: 2, lg: 3 },
breakpoints: { value: ['320vp', '600vp', '840vp'], reference: BreakpointsReference.WindowSize }
}) {
ForEach(group.items, (setting: SettingsType) => {
GridCol({ span: 1 }) {
this.SettingItem(setting)
}
})
}
})
}
在这个配置中:
- 在小屏和中小屏设备(xs和sm断点)上,设置项保持单列布局
- 在中等屏幕(md断点)上,设置项使用2列布局
- 在大屏设备(lg断点)上,设置项使用3列布局
3.2 自定义断点
除了使用默认的断点配置外,我们还可以根据具体需求自定义断点:
typescript
// 自定义断点配置
private customBreakpoints = {
value: ['320vp', '520vp', '720vp', '960vp'],
reference: BreakpointsReference.WindowSize
};
// 在build方法中使用自定义断点
GridRow({
columns: { xs: 1, sm: 1, md: 2, lg: 2, xl: 3 },
breakpoints: this.customBreakpoints
}) {
// ...
}
3.3 设置项的响应式布局
我们可以根据屏幕尺寸调整设置项的布局和样式:
typescript
@Builder
private SettingItem(setting: SettingsType, isSmallScreen: boolean = false) {
Row() {
Image(setting.icon)
.width(isSmallScreen ? 20 : 24)
.height(isSmallScreen ? 20 : 24)
.margin({ right: isSmallScreen ? 12 : 16 })
Column() {
Text(setting.title)
.fontSize(isSmallScreen ? 14 : 16)
if (setting.description) {
Text(setting.description)
.fontSize(isSmallScreen ? 10 : 12)
.fontColor('#999999')
.margin({ top: isSmallScreen ? 2 : 4 })
}
}
.alignItems(HorizontalAlign.Start)
Blank()
if (setting.hasSwitch) {
Toggle({ type: ToggleType.Switch, isOn: setting.isOn ?? false })
.margin({ left: 8 })
.onChange((isOn: boolean) => {
setting.isOn = isOn
})
} else {
Image($r("app.media.arrowright"))
.width(isSmallScreen ? 14 : 16)
.height(isSmallScreen ? 14 : 16)
}
}
.width('100%')
.padding(isSmallScreen ? 12 : 16)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.margin({ bottom: 8 })
}
然后在build方法中使用媒体查询来判断当前屏幕尺寸:
typescript
build() {
Column() {
// 标题...
ForEach(this.settingsGroups, (group: SettingsGroupType, groupIndex: number) => {
// 分组标题...
// 使用媒体查询判断屏幕尺寸
MediaQuery({ minWidth: 320, maxWidth: 520 }) {
GridRow({ columns: 1 }) {
ForEach(group.items, (setting: SettingsType) => {
GridCol({ span: 1 }) {
this.SettingItem(setting, true) // 小屏版本
}
})
}
}
MediaQuery({ minWidth: 521 }) {
GridRow({
columns: { sm: 1, md: 2, lg: 3 },
breakpoints: { value: ['600vp', '840vp'], reference: BreakpointsReference.WindowSize }
}) {
ForEach(group.items, (setting: SettingsType) => {
GridCol({ span: 1 }) {
this.SettingItem(setting, false) // 大屏版本
}
})
}
}
})
}
}
4. 设置选项的交互设计
4.1 点击效果
为设置项添加点击效果,使其更具交互性:
typescript
@Builder
private SettingItem(setting: SettingsType) {
Row() {
// 原有内容...
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.margin({ bottom: 8 })
.onClick(() => {
if (!setting.hasSwitch) {
// 处理点击事件
console.info(`${setting.title} clicked`)
}
})
.stateStyles({
pressed: {
.backgroundColor('#F0F0F0')
.scale({ x: 0.98, y: 0.98 })
}
})
}
4.2 悬停效果
在桌面设备上,我们可以为设置项添加悬停效果:
typescript
.stateStyles({
pressed: {
.backgroundColor('#F0F0F0')
.scale({ x: 0.98, y: 0.98 })
},
hover: {
.backgroundColor('#F8F8F8')
.shadow({
radius: 4,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 2
})
}
})
4.3 开关交互
为带开关的设置项添加交互逻辑:
typescript
@State settingsGroups: SettingsGroupType[] = [ /* ... */ ]
// 在SettingItem中
if (setting.hasSwitch) {
Toggle({ type: ToggleType.Switch, isOn: setting.isOn ?? false })
.margin({ left: 8 })
.onChange((isOn: boolean) => {
// 更新开关状态
setting.isOn = isOn
// 处理特定设置项的逻辑
if (setting.title === '深色模式') {
this.toggleDarkMode(isOn)
} else if (setting.title === '通知设置') {
this.toggleNotifications(isOn)
}
})
}
// 处理深色模式切换
private toggleDarkMode(isOn: boolean) {
// 实现深色模式切换逻辑
console.info(`深色模式: ${isOn ? '开启' : '关闭'}`)
}
// 处理通知设置切换
private toggleNotifications(isOn: boolean) {
// 实现通知设置切换逻辑
console.info(`通知设置: ${isOn ? '开启' : '关闭'}`)
}
5. 动画效果实现
5.1 列表项进入动画
为设置选项列表添加进入动画,使界面更加生动:
typescript
@State appearAnimation: boolean = false
aboutToAppear() {
// 延迟执行动画,确保组件已经渲染
setTimeout(() => {
this.appearAnimation = true
}, 100)
}
// 在SettingItem中添加动画
@Builder
private SettingItem(setting: SettingsType, index: number) {
Row() {
// 原有内容...
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.margin({ bottom: 8 })
.opacity(this.appearAnimation ? 1 : 0)
.animation({
duration: 300,
curve: Curve.EaseOut,
delay: 50 * index, // 根据索引设置延迟,创建级联效果
iterations: 1
})
// 其他属性...
}
// 在build方法中传递索引
ForEach(group.items, (setting: SettingsType, index: number) => {
GridCol({ span: 1 }) {
this.SettingItem(setting, index)
}
})
5.2 开关动画
为开关添加自定义动画效果:
typescript
@Builder
private AnimatedToggle(isOn: boolean, onChange: (isOn: boolean) => void) {
Toggle({ type: ToggleType.Switch, isOn: isOn })
.margin({ left: 8 })
.onChange((value: boolean) => {
onChange(value)
})
.animation({
duration: 200,
curve: Curve.EaseInOut,
iterations: 1
})
}
// 在SettingItem中使用AnimatedToggle
if (setting.hasSwitch) {
this.AnimatedToggle(setting.isOn ?? false, (isOn: boolean) => {
setting.isOn = isOn
// 处理特定设置项的逻辑...
})
}
6. 主题与样式定制
6.1 设置项样式变体
为设置项创建不同的样式变体:
typescript
enum SettingItemStyle {
Default,
Highlighted,
Outlined
}
@Builder
private SettingItem(setting: SettingsType, style: SettingItemStyle = SettingItemStyle.Default) {
Row() {
// 原有内容...
}
.width('100%')
.padding(16)
.backgroundColor(this.getItemBackground(style))
.borderRadius(8)
.border(style === SettingItemStyle.Outlined ? {
width: 1,
color: '#E0E0E0',
style: BorderStyle.Solid
} : null)
.margin({ bottom: 8 })
// 其他属性...
}
private getItemBackground(style: SettingItemStyle): string {
switch (style) {
case SettingItemStyle.Default:
return '#FFFFFF';
case SettingItemStyle.Highlighted:
return '#E3F2FD';
case SettingItemStyle.Outlined:
return 'transparent';
default:
return '#FFFFFF';
}
}
// 在build方法中使用不同的样式
ForEach(group.items, (setting: SettingsType, index: number) => {
GridCol({ span: 1 }) {
// 为特定设置项使用高亮样式
if (setting.title === '深色模式') {
this.SettingItem(setting, SettingItemStyle.Highlighted)
} else if (setting.title === '关于我们') {
this.SettingItem(setting, SettingItemStyle.Outlined)
} else {
this.SettingItem(setting)
}
}
})
6.2 自定义主题
创建自定义主题,使设置选项列表能够适应不同的应用风格:
typescript
@Observed
class SettingsTheme {
primaryColor: string = '#2196F3'
backgroundColor: string = '#F5F5F5'
cardBackgroundColor: string = '#FFFFFF'
textPrimaryColor: string = '#000000'
textSecondaryColor: string = '#666666'
textTertiaryColor: string = '#999999'
borderRadius: number = 8
isDark: boolean = false
// 切换暗色主题
toggleDarkMode() {
this.isDark = !this.isDark
if (this.isDark) {
this.backgroundColor = '#121212'
this.cardBackgroundColor = '#1E1E1E'
this.textPrimaryColor = '#FFFFFF'
this.textSecondaryColor = 'rgba(255, 255, 255, 0.7)'
this.textTertiaryColor = 'rgba(255, 255, 255, 0.5)'
} else {
this.backgroundColor = '#F5F5F5'
this.cardBackgroundColor = '#FFFFFF'
this.textPrimaryColor = '#000000'
this.textSecondaryColor = '#666666'
this.textTertiaryColor = '#999999'
}
}
}
@Component
export struct SettingsGrid {
@Provide theme: SettingsTheme = new SettingsTheme()
// 其他属性...
// 在SettingItem中使用主题属性
@Builder
private SettingItem(setting: SettingsType) {
Row() {
Image(setting.icon)
.width(24)
.height(24)
.margin({ right: 16 })
Column() {
Text(setting.title)
.fontSize(16)
.fontColor(this.theme.textPrimaryColor)
if (setting.description) {
Text(setting.description)
.fontSize(12)
.fontColor(this.theme.textTertiaryColor)
.margin({ top: 4 })
}
}
.alignItems(HorizontalAlign.Start)
Blank()
// 其他内容...
}
.width('100%')
.padding(16)
.backgroundColor(this.theme.cardBackgroundColor)
.borderRadius(this.theme.borderRadius)
.margin({ bottom: 8 })
// 其他属性...
}
// 在build方法中使用主题属性
build() {
Column() {
Text('设置')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(this.theme.textPrimaryColor)
.margin({ bottom: 16 })
.width('100%')
.textAlign(TextAlign.Start)
// 查找深色模式设置项并绑定到主题
ForEach(this.settingsGroups, (group: SettingsGroupType) => {
Text(group.groupTitle)
.fontSize(14)
.fontColor(this.theme.textSecondaryColor)
// 其他属性...
// 设置项...
})
}
.width('100%')
.padding(16)
.backgroundColor(this.theme.backgroundColor)
}
}
7. GridRow和GridCol的高级配置
7.1 嵌套网格
使用嵌套的GridRow和GridCol实现更复杂的布局:
typescript
// 在特定分组中使用嵌套网格
if (group.groupTitle === '通用') {
GridRow({ columns: 1 }) {
GridCol({ span: 1 }) {
GridRow({ columns: { xs: 1, sm: 2, md: 3 } }) {
ForEach(group.items, (setting: SettingsType) => {
GridCol({ span: 1 }) {
this.SettingItem(setting)
}
})
}
}
}
} else {
// 其他分组使用普通网格
GridRow({ columns: 1 }) {
ForEach(group.items, (setting: SettingsType) => {
GridCol({ span: 1 }) {
this.SettingItem(setting)
}
})
}
}
7.2 列偏移
使用offset属性实现列偏移,创建不对称的布局:
typescript
// 在特定分组中使用列偏移
if (group.groupTitle === '关于') {
GridRow({ columns: 12 }) {
GridCol({ span: 5 }) {
this.SettingItem(group.items[0])
}
GridCol({ span: 5, offset: 2 }) {
this.SettingItem(group.items[1])
}
}
} else {
// 其他分组使用普通网格
// ...
}
7.3 列顺序调整
使用order属性调整列的顺序:
typescript
// 在特定分组中调整列顺序
if (group.groupTitle === '账号') {
GridRow({ columns: 2 }) {
GridCol({ span: 1, order: 2 }) {
this.SettingItem(group.items[0])
}
GridCol({ span: 1, order: 1 }) {
this.SettingItem(group.items[1])
}
}
} else {
// 其他分组使用普通网格
// ...
}
8. 完整优化代码
以下是结合了分组、响应式布局、交互设计、动画效果和主题定制的完整优化代码:
typescript
// 设置选项列表网格布局(优化版)
@Observed
class SettingsTheme {
primaryColor: string = '#2196F3'
backgroundColor: string = '#F5F5F5'
cardBackgroundColor: string = '#FFFFFF'
textPrimaryColor: string = '#000000'
textSecondaryColor: string = '#666666'
textTertiaryColor: string = '#999999'
borderRadius: number = 8
isDark: boolean = false
// 切换暗色主题
toggleDarkMode() {
this.isDark = !this.isDark
if (this.isDark) {
this.backgroundColor = '#121212'
this.cardBackgroundColor = '#1E1E1E'
this.textPrimaryColor = '#FFFFFF'
this.textSecondaryColor = 'rgba(255, 255, 255, 0.7)'
this.textTertiaryColor = 'rgba(255, 255, 255, 0.5)'
} else {
this.backgroundColor = '#F5F5F5'
this.cardBackgroundColor = '#FFFFFF'
this.textPrimaryColor = '#000000'
this.textSecondaryColor = '#666666'
this.textTertiaryColor = '#999999'
}
}
}
interface SettingsType {
title: string;
icon: Resource;
description?: string;
hasSwitch?: boolean;
isOn?: boolean;
}
interface SettingsGroupType {
groupTitle: string;
items: SettingsType[];
}
enum SettingItemStyle {
Default,
Highlighted,
Outlined
}
@Component
export struct SettingsGrid {
@Provide theme: SettingsTheme = new SettingsTheme()
@State appearAnimation: boolean = false
@State settingsGroups: SettingsGroupType[] = [
{
groupTitle: '账号',
items: [
{ title: '账号与安全', icon: $r('app.media.01'), description: '管理您的账号信息和安全设置' },
{ title: '隐私设置', icon: $r('app.media.03'), description: '管理您的隐私和数据' }
]
},
{
groupTitle: '通知与声音',
items: [
{ title: '通知设置', icon: $r('app.media.02'), hasSwitch: true, isOn: true },
{ title: '声音设置', icon: $r('app.media.04'), hasSwitch: true, isOn: true }
]
},
{
groupTitle: '通用',
items: [
{ title: '通用设置', icon: $r('app.media.04') },
{ title: '语言', icon: $r('app.media.05'), description: '简体中文' },
{ title: '深色模式', icon: $r('app.media.01'), hasSwitch: true, isOn: false }
]
},
{
groupTitle: '关于',
items: [
{ title: '帮助与反馈', icon: $r('app.media.05') },
{ title: '关于我们', icon: $r('app.media.01'), description: '版本 1.0.0' }
]
}
]
private customBreakpoints = {
value: ['320vp', '520vp', '720vp', '960vp'],
reference: BreakpointsReference.WindowSize
};
aboutToAppear() {
// 延迟执行动画,确保组件已经渲染
setTimeout(() => {
this.appearAnimation = true
}, 100)
// 查找深色模式设置项并绑定到主题
this.findAndBindDarkModeToggle()
}
private findAndBindDarkModeToggle() {
for (let group of this.settingsGroups) {
for (let item of group.items) {
if (item.title === '深色模式') {
item.isOn = this.theme.isDark
break
}
}
}
}
@Builder
private SettingItem(setting: SettingsType, index: number = 0, style: SettingItemStyle = SettingItemStyle.Default) {
Row() {
Image(setting.icon)
.width(24)
.height(24)
.margin({ right: 16 })
Column() {
Text(setting.title)
.fontSize(16)
.fontColor(this.theme.textPrimaryColor)
if (setting.description) {
Text(setting.description)
.fontSize(12)
.fontColor(this.theme.textTertiaryColor)
.margin({ top: 4 })
}
}
.alignItems(HorizontalAlign.Start)
Blank()
if (setting.hasSwitch) {
this.AnimatedToggle(setting.isOn ?? false, (isOn: boolean) => {
setting.isOn = isOn
// 处理特定设置项的逻辑
if (setting.title === '深色模式') {
this.theme.toggleDarkMode()
} else if (setting.title === '通知设置') {
this.toggleNotifications(isOn)
}
})
} else {
Image($r("app.media.arrowright"))
.width(16)
.height(16)
}
}
.width('100%')
.padding(16)
.backgroundColor(this.getItemBackground(style))
.borderRadius(this.theme.borderRadius)
.border(style === SettingItemStyle.Outlined ? {
width: 1,
color: '#E0E0E0',
style: BorderStyle.Solid
} : null)
.margin({ bottom: 8 })
.opacity(this.appearAnimation ? 1 : 0)
.animation({
duration: 300,
curve: Curve.EaseOut,
delay: 50 * index,
iterations: 1
})
.onClick(() => {
if (!setting.hasSwitch) {
// 处理点击事件
console.info(`${setting.title} clicked`)
}
})
.stateStyles({
pressed: {
.backgroundColor(style === SettingItemStyle.Default ? '#F0F0F0' : this.getItemBackground(style))
.scale({ x: 0.98, y: 0.98 })
.opacity(0.9)
},
hover: {
.backgroundColor(style === SettingItemStyle.Default ? '#F8F8F8' : this.getItemBackground(style))
.shadow({
radius: 4,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 2
})
}
})
}
@Builder
private AnimatedToggle(isOn: boolean, onChange: (isOn: boolean) => void) {
Toggle({ type: ToggleType.Switch, isOn: isOn })
.margin({ left: 8 })
.onChange((value: boolean) => {
onChange(value)
})
.animation({
duration: 200,
curve: Curve.EaseInOut,
iterations: 1
})
}
private getItemBackground(style: SettingItemStyle): string {
if (this.theme.isDark) {
switch (style) {
case SettingItemStyle.Default:
return this.theme.cardBackgroundColor;
case SettingItemStyle.Highlighted:
return '#1A237E';
case SettingItemStyle.Outlined:
return 'transparent';
default:
return this.theme.cardBackgroundColor;
}
} else {
switch (style) {
case SettingItemStyle.Default:
return this.theme.cardBackgroundColor;
case SettingItemStyle.Highlighted:
return '#E3F2FD';
case SettingItemStyle.Outlined:
return 'transparent';
default:
return this.theme.cardBackgroundColor;
}
}
}
// 处理通知设置切换
private toggleNotifications(isOn: boolean) {
// 实现通知设置切换逻辑
console.info(`通知设置: ${isOn ? '开启' : '关闭'}`)
}
build() {
Column() {
Text('设置')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(this.theme.textPrimaryColor)
.margin({ bottom: 16 })
.width('100%')
.textAlign(TextAlign.Start)
ForEach(this.settingsGroups, (group: SettingsGroupType, groupIndex: number) => {
// 分组标题
Text(group.groupTitle)
.fontSize(14)
.fontColor(this.theme.textSecondaryColor)
.margin({ top: groupIndex > 0 ? 24 : 0, bottom: 8, left: 8 })
.width('100%')
.textAlign(TextAlign.Start)
.opacity(this.appearAnimation ? 1 : 0)
.animation({
duration: 300,
curve: Curve.EaseOut,
delay: 50 * groupIndex,
iterations: 1
})
// 使用媒体查询判断屏幕尺寸
MediaQuery({ minWidth: 320, maxWidth: 520 }) {
// 小屏设备使用单列布局
GridRow({ columns: 1 }) {
ForEach(group.items, (setting: SettingsType, index: number) => {
GridCol({ span: 1 }) {
// 为特定设置项使用特殊样式
if (setting.title === '深色模式') {
this.SettingItem(setting, index, SettingItemStyle.Highlighted)
} else if (setting.title === '关于我们') {
this.SettingItem(setting, index, SettingItemStyle.Outlined)
} else {
this.SettingItem(setting, index)
}
}
})
}
}
MediaQuery({ minWidth: 521 }) {
// 根据分组使用不同的布局
if (group.groupTitle === '通用') {
// 通用分组使用3列布局
GridRow({
columns: { sm: 1, md: 3, lg: 3 },
breakpoints: this.customBreakpoints
}) {
ForEach(group.items, (setting: SettingsType, index: number) => {
GridCol({ span: 1 }) {
if (setting.title === '深色模式') {
this.SettingItem(setting, index, SettingItemStyle.Highlighted)
} else {
this.SettingItem(setting, index)
}
}
})
}
} else if (group.groupTitle === '关于') {
// 关于分组使用列偏移
GridRow({ columns: 12 }) {
GridCol({ span: 5 }) {
this.SettingItem(group.items[0], 0)
}
GridCol({ span: 5, offset: 2 }) {
this.SettingItem(group.items[1], 1, SettingItemStyle.Outlined)
}
}
} else if (group.groupTitle === '账号') {
// 账号分组调整列顺序
GridRow({ columns: 2 }) {
GridCol({ span: 1, order: 2 }) {
this.SettingItem(group.items[0], 0)
}
GridCol({ span: 1, order: 1 }) {
this.SettingItem(group.items[1], 1)
}
}
} else {
// 其他分组使用2列布局
GridRow({
columns: { sm: 1, md: 2, lg: 2 },
breakpoints: this.customBreakpoints
}) {
ForEach(group.items, (setting: SettingsType, index: number) => {
GridCol({ span: 1 }) {
this.SettingItem(setting, index)
}
})
}
}
}
})
}
.width('100%')
.padding(16)
.backgroundColor(this.theme.backgroundColor)
}
}
10. 总结
本教程详细讲解了如何优化设置选项列表网格布局,添加分组、响应式支持、交互设计、动画效果和主题定制。通过使用HarmonyOS NEXT的GridRow和GridCol组件的高级特性,我们实现了一个功能丰富、美观灵活的设置选项列表。