一、引言

开关(Toggle)是移动端设置页面中最基础的交互元素。从 iOS 设置中的 Wi-Fi 开关到 Android 通知栏的蓝牙图标,从 App 的深色模式切换到隐私设置的定位授权——一个简单的开/关二元选择,承载了用户对应用行为的最直接控制。

在 HarmonyOS NEXT 之前,开发者若要实现开关组件,通常使用 Switch(滑动开关)或 Checkbox(勾选框)等分散组件——它们的 API 不完全一致,类型之间无法统一管理。ArkUI 在 API 10 中推出了统一的 Toggle 组件,将三种开关形态(滑动开关、勾选框、按钮开关)整合到同一个组件接口下,通过 ToggleType 参数切换形态,使用统一的 isOnonChange 管理状态。

本文通过一个应用功能开关面板 Demo 深入讲解 Toggle 组件的核心用法:三种类型的区别和适用场景、开关联动禁用、状态计数和恢复默认。阅读完本文,你将能够:

  • 使用 Toggle 组件的三种类型(Switch / Checkbox / Button)
  • isOnonChange 管理开关状态
  • 实现开关之间的联动逻辑(父子开关的自动禁用)
  • 实现全局状态统计和一键恢复默认

二、Toggle 组件 API 总览

2.1 构造函数参数

Toggle({ type: ToggleType.Switch, isOn: true })
参数 类型 说明
type ToggleType 开关类型:Switch(滑动开关)、Checkbox(勾选框)、Button(按钮开关)
isOn boolean 开关初始状态。true = 开启,false = 关闭

Toggle 是受控组件——它不维护内部状态,始终反映外部传入的 isOn 值。当用户点击 Toggle 时,视觉状态改变,但 isOn 值不会自动更新;开发者需要在 onChange 中手动更新 isOn 以保持 UI 一致性。

2.2 三种类型对比

ToggleType.Switch — 滑动开关

Toggle({ type: ToggleType.Switch, isOn: true })
  .selectedColor('#1677FF')

滑动开关是一个椭圆形滑块——左=关(灰色),右=开(蓝色)。这是最常见的开关形态,iOS 和 Android 都使用这种样式作为设置的默认开关。

Switch 的优势是视觉最直观——一眼就能看出当前状态(开=有色,关=灰色)。适合需要快速扫视的开关密集场景(如设置列表)。

ToggleType.Checkbox — 勾选框

Toggle({ type: ToggleType.Checkbox, isOn: false })
  .selectedColor('#1677FF')

勾选框是一个正方形框,选中时内部打勾。Checkbox 的视觉不如 Switch 直观(需要看清框内是否有勾),但它的文化认知度更高——在表单、协议同意、多选列表中,Checkbox 是用户最熟悉的交互模式。

Checkbox 适合"确认型"场景——如"我已阅读并同意协议"、“启用声音提醒”。这些场景中,用户不是在"快速切换",而是在"确认一个选择"。

ToggleType.Button — 按钮开关

Toggle({ type: ToggleType.Button, isOn: false })
  .selectedColor('#1677FF')

按钮开关是一个圆角矩形按钮,按下态和弹起态有明显区别。Button 类型是三兄弟中最"重"的交互——它有一个实体按钮的外形和按下反馈,适合需要强调的开关(如"振动反馈"——用户需要明确感知这个功能的开启/关闭)。

类型 视觉形态 适用场景 交互感觉
Switch 椭圆滑块 设置列表(快速开关) 轻量、直观
Checkbox 正方形勾选框 确认操作(协议、选项) 传统、可靠
Button 圆角按钮 需要强调的功能开关 实体、确定

在同一个设置页面中混合使用不同类型是合理的——根据开关的"重要性"选择最合适的形态。例如"消息推送"用 Switch(高频率主开关),"声音提醒"用 Checkbox(子选项确认),"振动反馈"用 Button(需要明确感知)。

2.3 属性方法

selectedColor(value: ResourceColor)

设置开启状态的颜色:

Toggle({ type: ToggleType.Switch, isOn: true })
  .selectedColor('#1677FF')  // 蓝色开启态

selectedColor 是 Toggle 最常用的属性——它决定了开关的"主题色"。在 iOS 中默认为绿色(#34C759),在 Android 中默认为蓝色(#1976D2),在 HarmonyOS 中默认为系统主题色。推荐使用应用的品牌色或语义色。

enabled(value: boolean)

设置开关是否可交互:

Toggle({ type: ToggleType.Switch, isOn: false })
  .enabled(false)  // 禁用,灰色且不可点击

禁用的 Toggle 显示为灰色,用户点击无效。这是实现联动逻辑的关键属性——当父开关关闭时,子开关自动禁用。

2.4 事件回调

onChange(callback: (isOn: boolean) => void)

开关状态改变时触发:

Toggle({ type: ToggleType.Switch, isOn: this.pushEnabled })
  .onChange((isOn: boolean) => {
    this.pushEnabled = isOn;
    if (!isOn) {
      // 联动:关闭推送时,声音和振动也关闭
      this.soundEnabled = false;
      this.vibrateEnabled = false;
    }
  })

onChange 是 Toggle 的核心事件——用户点击开关后,回调接收新的 isOn 值。开发者需要在回调中做两件事:

  1. 更新对应状态变量(保证 Toggle 视觉与数据一致)
  2. 执行副作用(联动其他开关、保存设置、发送埋点等)
    在这里插入图片描述

三、Demo 设计:应用功能开关面板

3.1 功能概述

Demo 模拟一个应用功能开关面板,包含 8 个设置项,分为 3 组:

消息通知组(3 项)

开关 类型 默认值 说明
消息推送 Switch 开启 主开关,关闭时联动禁用子项
声音提醒 Checkbox 开启 子开关,推送关闭时自动禁用
振动反馈 Button 关闭 子开关,推送关闭时自动禁用

隐私设置组(3 项)

开关 类型 默认值 说明
位置共享 Switch 关闭 敏感权限,默认关闭
个性化推荐 Switch 开启 可选功能
数据统计 Switch 开启 默认开启帮助改进产品

显示偏好组(2 项)

开关 类型 默认值 说明
深色模式 Switch 关闭 视觉偏好
列表紧凑模式 Switch 关闭 信息密度偏好

顶部状态栏实时显示"已开启 X/8 项",底部"恢复默认"按钮一键重置所有开关到默认值。

3.2 数据结构

interface SettingItem {
  key: string;       // 唯一标识
  label: string;     // 显示名称
  desc: string;      // 描述文字
  isOn: boolean;     // 当前状态
  defaultOn: boolean;// 默认状态(恢复默认时使用)
  group: string;     // 分组
  type: ToggleType;  // 开关类型
  disabled: boolean; // 是否禁用
}

@State settings: SettingItem[] = [];
@State enabledCount: number = 0;

defaultOn 字段保存了每个开关的默认值——“恢复默认"功能依赖它。如果只保存 isOn 而不保存 defaultOn,就无法区分"用户手动关闭的"和"默认就是关闭的”。

disabled 字段由联动逻辑控制——当父开关(消息推送)关闭时,子开关(声音提醒、振动反馈)的 disabled 变为 true

3.3 三种 Toggle 类型的实际使用

在 Demo 中,三种类型各有一个实例,各自服务于不同的交互场景:

Switch 类型(消息推送)

Toggle({ type: ToggleType.Switch, isOn: item.isOn })
  .selectedColor('#1677FF')
  .enabled(!item.disabled)
  .onChange((isOn: boolean) => {
    this.onToggleChange(item.key, isOn);
  })

Switch 是最高频使用的开关类型——8 个设置中 6 个使用 Switch。它们的特点是:视觉直观、操作快、适合列表扫描。

Checkbox 类型(声音提醒)

Toggle({ type: ToggleType.Checkbox, isOn: item.isOn })
  .selectedColor('#1677FF')
  .enabled(!item.disabled)
  .onChange((isOn: boolean) => {
    this.onToggleChange(item.key, isOn);
  })

Checkbox 用于"声音提醒"——它是"消息推送"的子选项,使用 Checkbox 而非 Switch 有三个原因:

  1. 视觉区分:在同一组中,主开关用 Switch,子开关用 Checkbox,用户自然建立层级感
  2. 确认语义:声音提醒不是高频切换项(用户不会频繁开关声音),Checkbox 的"确认"语义比 Switch 的"快速切换"更适合
  3. 文化习惯:声音相关设置在许多应用中都用 Checkbox(如 Android 的"铃声"设置)

Button 类型(振动反馈)

Toggle({ type: ToggleType.Button, isOn: item.isOn })
  .selectedColor('#1677FF')
  .width(48)
  .enabled(!item.disabled)
  .onChange((isOn: boolean) => {
    this.onToggleChange(item.key, isOn);
  })

Button 用于"振动反馈"——振动是一种触觉体验,Button 类型的按压反馈与振动的"触觉"语义一致。此外,振动的默认值是关闭的——这意味着大多数用户不会开启它,而 Button 类型的"实体感"让用户更慎重地考虑是否开启。

3.4 联动逻辑

消息通知组是 Demo 的核心交互——关闭"消息推送"时,声音和振动自动禁用:

onToggleChange(key: string, isOn: boolean): void {
  const newSettings: SettingItem[] = [];
  for (let i = 0; i < this.settings.length; i++) {
    const s = this.settings[i];
    if (s.key === key) {
      // 更新触发的开关
      newSettings.push({ ...s, isOn: isOn });
    } else if (s.key === 'sound' || s.key === 'vibrate') {
      if (key === 'push' && !isOn) {
        // 推送关闭 → 子开关关闭且禁用
        newSettings.push({ ...s, isOn: false, disabled: true });
      } else if (key === 'push' && isOn) {
        // 推送打开 → 子开关恢复默认且启用
        newSettings.push({ ...s, isOn: s.defaultOn, disabled: false });
      } else {
        newSettings.push(s);
      }
    } else {
      newSettings.push(s);
    }
  }
  this.settings = newSettings;
  this.countEnabled();
}

关键逻辑:

  1. 推送关闭时:声音和振动强制设为 falsedisabled 设为 true
  2. 推送重新开启时:声音和振动恢复到各自的默认值(声音=true,振动=false),disabled 恢复 false
  3. 其他开关的变化不影响子开关

这种"推送关闭 → 子开关禁用"的联动逻辑在很多真实应用中出现——iOS 的"通知"设置中,关闭"允许通知"会自动隐藏所有子选项。这里没有隐藏子开关而是显示为禁用态,因为禁用态能告知用户"这里还有选项,只是当前不可用"——比直接隐藏更有引导性。

3.5 状态计数

页面顶部的状态栏实时显示开启数量:

countEnabled(): void {
  let n = 0;
  for (let i = 0; i < this.settings.length; i++) {
    if (this.settings[i].isOn) { n++; }
  }
  this.enabledCount = n;
}

每次 onToggleChangeresetAll 执行后调用 countEnabled 更新计数。这个数字提供了"全局视野"——用户无需逐一数开关,就能知道有多少功能处于开启状态。

3.6 恢复默认

resetAll(): void {
  const newSettings: SettingItem[] = [];
  for (let i = 0; i < this.settings.length; i++) {
    const s = this.settings[i];
    newSettings.push({
      ...s, isOn: s.defaultOn, disabled: false
    });
  }
  this.settings = newSettings;
  this.countEnabled();
  // Toast 提示
  this.showResetToast = true;
  this.toastTimer = setInterval(() => {
    clearInterval(this.toastTimer);
    this.toastTimer = -1;
    this.showResetToast = false;
  }, 2000);
}

恢复默认将所有 isOn 重置为 defaultOn,所有 disabled 恢复 false。然后用一个 2 秒的 Toast(底部黑色文字条)给予视觉确认——“✓ 已恢复默认设置”。Toast 使用 setInterval 自清理模式,2 秒后自动消失。

3.7 页面结构

┌──────────────────────────────────────────┐
│ ⚙ 功能开关(深色标题栏)                   │
├──────────────────────────────────────────┤
│ 📘 Toggle 组件说明卡片                     │
├──────────────────────────────────────────┤
│ ┌────────────────────────────────────┐   │
│ │ 已开启 5/8 项          恢复默认    │   │ ← 状态计数 + 恢复按钮
│ └────────────────────────────────────┘   │
├──────────────────────────────────────────┤
│ 消息通知                                  │ ← 分组标题
│ ┌────────────────────────────────────┐   │
│ │ 消息推送                    [====] │   │ ← Switch(主开关)
│ │ 接收新消息和系统通知                │   │
│ │────────────────────────────────────│   │
│ │ 声音提醒                    [✓]    │   │ ← Checkbox(子开关)
│ │ 收到消息时播放提示音                │   │
│ │────────────────────────────────────│   │
│ │ 振动反馈                   [按钮]  │   │ ← Button(子开关)
│ │ 收到消息时振动设备                  │   │
│ └────────────────────────────────────┘   │
├──────────────────────────────────────────┤
│ 隐私设置                                  │
│ ┌────────────────────────────────────┐   │
│ │ 位置共享                    [  ]   │   │
│ │ 个性化推荐                  [====] │   │
│ │ 数据统计                    [====] │   │
│ └────────────────────────────────────┘   │
├──────────────────────────────────────────┤
│ 显示偏好                                  │
│ ┌────────────────────────────────────┐   │
│ │ 深色模式                    [  ]   │   │
│ │ 列表紧凑模式                [  ]   │   │
│ └────────────────────────────────────┘   │
├──────────────────────────────────────────┤
│        [✓ 已恢复默认设置](Toast)        │
└──────────────────────────────────────────┘

在这里插入图片描述

四、Toggle 组件的最佳实践

4.1 三种类型的选择指南

场景 推荐类型 理由
主功能开关(高频率) Switch 视觉最直观,操作最快
子选项确认 Checkbox 文化和交互的"确认"语义
需要强调的功能 Button 实体按钮反馈,操作慎重
隐私/安全相关 Switch + 二次确认 Switch 快速看到状态,确认弹窗防止误操作
列表多选 Checkbox 多选是 Checkbox 的核心场景
协议同意 Checkbox "我已阅读"是确认行为,不是开关行为

4.2 联动设计

当多个开关存在关联关系时,有几种联动策略:

策略 1:自动禁用子开关(本 Demo 采用)

父开关关闭 → 子开关变灰且不可操作,但保留位置。适用场景:用户需要知道"还有这些子选项",但当前不可用。

策略 2:自动隐藏子开关

父开关关闭 → 子开关直接消失。适用场景:主开关关闭时子开关完全无意义(如"关闭 Wi-Fi"隐藏所有 Wi-Fi 网络列表)。

策略 3:子开关独立存在

父开关不联动子开关。适用场景:子开关即使脱离父开关也有独立的意义。

选择哪种策略取决于"用户是否需要看到被禁用的选项"。一般来说,如果子选项有独立的说明文字(如"收到消息时播放提示音"),禁用态比隐藏态更能教育用户。

4.3 默认值设计

默认值的选择应遵循"最小权限"原则:

  • 敏感功能默认关闭:位置共享、麦克风权限、通知推送等——用户应主动选择开启
  • 改善体验的功能默认开启:数据统计(匿名)、缓存优化——用户不需要感知
  • 视觉偏好默认关闭:深色模式、紧凑布局——除非系统级深色模式已开启

本 Demo 中,隐私三项有两项默认开启(个性化推荐、数据统计),它们不是"权限型"开关,而是"偏好型"开关——默认开启不会对用户造成困扰。

4.4 开关文案

Toggle 组件的文字标签应遵循:

  • 简短:控制在 4-6 个字(“消息推送”、“位置共享”、“深色模式”)
  • 正面表述:使用"开启 XXX"而非"关闭 XXX"——开关的开启态代表正面行为
  • 配描述:每个标签下方配一行小字的描述(12sp),帮助用户理解开关的具体效果

五、完整代码结构

TogglePage (~200 行)
├── 数据定义
│   └── SettingItem 接口 — key/label/desc/isOn/defaultOn/group/type/disabled
├── 状态变量
│   ├── @State settings[8] — 8 个设置项
│   ├── @State enabledCount — 开启计数
│   └── @State showResetToast — 恢复确认提示
├── 生命周期
│   ├── aboutToAppear() — 初始化 8 个设置项
│   └── aboutToDisappear() — 清理 Toast 定时器
├── 业务逻辑
│   ├── onToggleChange() — 更新开关 + 联动逻辑
│   ├── countEnabled() — 统计开启数量
│   └── resetAll() — 恢复默认 + Toast 提示
├── 视图
│   ├── 标题栏 — ⚙ 功能开关
│   ├── 说明卡片 — Toggle 组件介绍
│   ├── 状态栏 — 计数 + 恢复按钮
│   ├── 消息通知组(Switch/Checkbox/Button 各一)
│   ├── 隐私设置组(3 个 Switch)
│   ├── 显示偏好组(2 个 Switch)
│   └── Toast 浮层(条件渲染 + position)
└── @Builder
    ├── settingGroup() — 分组容器
    └── settingRow() — 单行开关(Toggle + 标签 + 描述 + 分割线)

六、总结

本文通过一个应用功能开关面板 Demo 深入讲解了 HarmonyOS NEXT 中的 Toggle 开关组件。Toggle 将 Switch、Checkbox、Button 三种开关形态统一到同一组件接口下,通过 type 切换形态,通过 isOn 控制状态,通过 onChange 监听变化。

核心要点回顾:

  1. 三种类型各有语义:Switch 适合高频快速开关(设置列表),Checkbox 适合确认型操作(子选项、协议),Button 适合需要强调的功能(需要慎重开启/关闭)。不同类型的本质是交互心理的差异。

  2. Toggle 是受控组件:它不维护内部状态——始终反映外部传入的 isOn 值。开发者必须在 onChange 中更新状态变量,否则 Toggle 的视觉与数据会不一致。

  3. 联动逻辑通过不可变更新实现:关闭父开关时,遍历数组,修改子开关的 isOndisabled 字段,生成新数组赋值给 @State。这种模式保证了联动逻辑的清晰性和可追溯性。

  4. 状态计数提供全局视野:"已开启 X/8 项"让用户无需逐一数开关就能了解全局状态。每次状态变更后调用 countEnabled 更新计数。

  5. 恢复默认需要保存初始值:每个设置项增加 defaultOn 字段记录默认状态,resetAll 将所有 isOn 重置为 defaultOn。2 秒 Toast 给予操作确认。

Toggle 组件是 ArkUI 中"统一接口、多种形态"设计哲学的典型代表——一个组件名、一套属性方法、一个事件回调,覆盖三种交互形态。这种设计让开发者不需要为 Switch 和 Checkbox 记忆两套不同的 API,也让页面上不同形态的开关可以共享相同的状态管理逻辑。

七、扩展思考

Toggle 组件解决了开关的 UI 和交互问题,但在真实设置页面中还有更多设计维度:

开关状态持久化:当前 Demo 的状态仅存在于页面生命周期——页面退出后状态丢失。在实际应用中,需要通过 @ohos.data.preferences 将开关状态持久化到本地存储,下次进入时恢复。

开关分组折叠:当设置项超过 10 个时,通常需要将分组折叠/展开,只显示当前活跃的分组。这与 Toggle 组件本身无关,是列表布局层的优化。

开关与用户权限的联动:HarmonyOS 的敏感权限(如位置、相机)有系统级授权弹窗。当用户开启"位置共享"时,应触发系统权限请求——Toggle 的 onChange 是触发权限请求的理想时机。

批量操作:除了"恢复默认",还可以提供"全部开启"、"全部关闭"等批量操作。这在多选项设置中比逐一操作更高效。

这些扩展说明:Toggle 是开关交互的最小单元,而一个完整的设置页面还需要状态持久化、权限集成、批量操作等上层逻辑。理解 Toggle 的核心机制(类型选择、受控模式、联动逻辑)是构建任何设置页面的基础。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐