一、引言

在移动端应用中,选择是仅次于输入的交互方式。无论是设置页中切换语言和主题、筛选页中选择分类和排序方式、还是表单中选择性别和地区——用户不需要输入新内容,只需要从预设的选项中"选中一个"。这类场景中,最好的 UI 方案不是文本框,而是下拉选择器

下拉选择器在桌面端是一个普通的控件事;但在移动端,它需要优雅地处理屏幕空间:点击时弹出一个选项菜单,选择后自动收起,菜单的展开方向、对齐方式、样式都需要精心处理。传统实现需要组合 Text + 箭头图标 + 弹出菜单 + 状态管理——即使功能简单,代码复杂度也不低。

HarmonyOS 提供了 Select 组件——一个专为下拉选择场景设计的组件。开发者只需声明选项数组和选中索引,Select 自动完成按钮渲染、菜单弹出、选中回调和样式管理。无需手动管理菜单的显示/隐藏状态,无需计算菜单的弹出位置——一切由 Select 内置处理。

本文通过一个个人偏好设置 Demo 深入讲解 Select 组件的核心用法:如何配置选项列表?如何设置默认选中?如何自定义选项和按钮的字体样式?如何通过 onSelect 回调获取用户选择?

阅读完本文,你将能够:

  • 使用 Select 组件替代手动布局的 Text + 图标 + 弹出菜单方案
  • 使用 selected 方法设置默认选中项
  • 使用 font/fontColor/optionFont 等样式方法定制外观
  • 通过 onSelect 回调处理用户的选择
  • 在设置页面中批量使用 Select 构建完整的选项管理

二、Select 组件 API 总览

2.1 构造函数

Select(options: Array<SelectOption>)

Select 的构造函数接收一个选项数组,定义了用户可选择的所有选项:

interface SelectOption {
  value: ResourceStr;          // 选项文本(必填)
  icon?: ResourceStr;          // 选项图标(可选)
  symbolIcon?: SymbolGlyphModifier;  // Symbol 图标(可选)
}

最简用法——仅需 value:

Select([
  { value: '中文' },
  { value: 'English' },
  { value: '日本語' }
])

2.2 核心方法一览

Select 的方法分为三类:选中状态控制、按钮样式、选项菜单样式。

方法 用途 示例
selected(index) 设置当前选中项的索引(从 0 开始) .selected(0)
value(text) 设置 Select 按钮上显示的文本 .value('中文')
font(font) 按钮文本的字体样式 .font({ size: 14 })
fontColor(color) 按钮文本的颜色 .fontColor('#1677FF')
onSelect(callback) 选项被选中时的回调 .onSelect((idx, val) => {})

选项菜单样式方法:

方法 用途
optionFont(font) 选项文本的字体
optionFontColor(color) 选项文本的颜色
optionBgColor(color) 选项的背景色
selectedOptionFont(font) 已选中选项的字体
selectedOptionFontColor(color) 已选中选项的文字颜色
selectedOptionBgColor(color) 已选中选项的背景色
menuBackgroundColor(color) 下拉菜单的背景色

布局控制方法:

方法 用途
arrowPosition(pos) 箭头位置:END(右侧,默认) / START(左侧)
space(length) 按钮中文字和图标的间距
menuAlign(align, offset) 菜单与按钮的对齐方式和偏移
optionWidth(width) 选项的宽度
optionHeight(height) 选项的高度

2.3 基本用法模式

@State selectedIdx: number = 0;

Select([
  { value: '选项A' },
  { value: '选项B' },
  { value: '选项C' }
])
  .selected(this.selectedIdx)
  .value(this.selectedIdx === 0 ? '选项A' : this.selectedIdx === 1 ? '选项B' : '选项C')
  .font({ size: 14, weight: FontWeight.Bold })
  .fontColor('#1677FF')
  .onSelect((index: number, value: string) => {
    this.selectedIdx = index;
  })

使用模式的关键:

  1. selected(index)value(text) 绑定到同一个 @State 变量
  2. onSelect 回调中更新 @State,触发 selectedvalue 同步更新
  3. 如果选项数量较多,用辅助方法替代三元表达式:value(this.getText())

2.4 为什么需要 selected 和 value 两个参数

Select 有两个看似重复的方法,但它们各司其职:

  • selected(index):控制打开菜单时哪个选项显示为选中状态(高亮/打勾)
  • value(text):控制 Select 按钮折叠时显示的文本

两者默认使用选项对应文本,但在某些场景下需要不同——例如按钮上显示简短文本,选项菜单中显示完整标签。大多数情况下两者保持一致即可。

2.5 选项样式的渲染层级

Select 的样式方法有三层:

  1. 通用选项样式optionFontoptionFontColor)——应用于全部选项
  2. 已选中选项样式selectedOptionFontselectedOptionFontColor)——仅应用于当前选中的选项,覆盖通用样式
  3. 按钮自身样式fontfontColor)——应用于 Select 折叠时的按钮文本,与选项样式独立

这意味着你可以让下拉菜单中的选中项用蓝色标注,同时按钮上的文本保持黑色——两者完全独立。
在这里插入图片描述

三、Demo 设计:个人偏好设置

3.1 功能概述

Demo 是一个个人偏好设置页面,模拟 App 设置中心的外观和交互:

  1. 外观设置组:4 个 Select——语言、主题、字体大小、字体颜色
  2. 通知设置组:1 个 Select——推送频率
  3. 当前配置摘要:实时展示 5 个选择器的当前选中值
  4. 分组卡片布局:每组设有标题和说明,模拟真实设置页的视觉结构

所有 5 个 Select 选择器均可独立操作。每次选择即时反映在选择结果和摘要栏中。

3.2 选项数据定义

private langOptions: SelectOption[] = [
  { value: '中文 (简体)' },
  { value: 'English' },
  { value: '日本語' }
];

private themeOptions: SelectOption[] = [
  { value: '跟随系统' },
  { value: '浅色模式' },
  { value: '深色模式' }
];

private fontSizeOptions: SelectOption[] = [
  { value: '小号' },
  { value: '标准' },
  { value: '大号' },
  { value: '特大号' }
];

private notifyOptions: SelectOption[] = [
  { value: '实时推送' },
  { value: '每小时汇总' },
  { value: '每日 digest' },
  { value: '从不通知' }
];

每个选项数组使用 SelectOption 接口定义。当前仅使用 value 字段(文本)。在高级场景中可以添加 icon 字段为每个选项显示图标。

3.3 Select 的统一封装

Demo 使用 @Builder settingRow() 封装了每个设置行的布局:左侧是设置名称和说明文字,右侧是 Select 组件。Select 组件通过 selectBuilder 回调参数传入,保持了设置的灵活性:

@Builder
settingRow(label: string, desc: string, selectBuilder: () => void) {
  Row() {
    Column() {
      Text(label).fontSize(15).fontColor('#1a1a2e').fontWeight(FontWeight.Bold)
      Text(desc).fontSize(11).fontColor('#9999AA')
    }
    .alignItems(HorizontalAlign.Start)
    .layoutWeight(1)

    selectBuilder()
  }
  .width('100%')
  .padding({ top: 14, bottom: 14, left: 4, right: 4 })
}

所有 5 个 Select 都使用统一的样式配置:

Select(this.langOptions)
  .selected(this.languageIdx)
  .value(this.langText())
  .font({ size: 14, weight: FontWeight.Bold })
  .fontColor('#1677FF')
  .selectedOptionFont({ size: 14 })
  .selectedOptionFontColor('#1677FF')
  .optionFont({ size: 14 })
  .optionFontColor('#1a1a2e')
  .onSelect((index: number) => { this.languageIdx = index; })

统一的样式让设置页保持视觉一致性——所有 Select 按钮的字体大小、颜色、选项样式完全相同。

3.4 当前配置摘要

页面底部的"当前配置"卡片展示所选设置:

Column() {
  Text('当前配置')
    .fontSize(12).fontColor('#9999AA').fontWeight(FontWeight.Bold)

  Column() {
    this.summaryRow('语言', this.langText())
    Divider().strokeWidth(0.5).color('#F2F3F5')
    this.summaryRow('主题', this.themeText())
    Divider().strokeWidth(0.5).color('#F2F3F5')
    this.summaryRow('字体大小', this.fontSizeText())
    Divider().strokeWidth(0.5).color('#F2F3F5')
    this.summaryRow('推送频率', this.notifyText())
  }
}

摘要栏实时反映每个 Select 的当前值——选择器变更触发 @State 更新,摘要栏自动刷新。这使得用户可以看到整体配置的全景视图。

3.5 页面结构

┌──────────────────────────────────────────┐
│ ⚙️ 偏好设置(深色标题栏)                 │
├──────────────────────────────────────────┤
│ 📘 Select 组件说明卡片                    │
├──────────────────────────────────────────┤
│ ┌ 外观 ───────────────────────────┐     │
│ │ 语言         [中文 (简体) ▼]    │     │
│ │ 设置界面显示语言                │     │
│ │─────────────────────────────────│     │
│ │ 主题         [跟随系统 ▼]       │     │
│ │ 控制应用外观颜色方案            │     │
│ │─────────────────────────────────│     │
│ │ 字体大小     [标准 ▼]           │     │
│ │ 调整界面文字显示大小            │     │
│ │─────────────────────────────────│     │
│ │ 字体颜色     [系统默认 ▼]       │     │
│ │ 调整界面文字颜色主题            │     │
│ └─────────────────────────────────┘     │
├──────────────────────────────────────────┤
│ ┌ 通知 ───────────────────────────┐     │
│ │ 推送频率     [实时推送 ▼]       │     │
│ │ 控制消息通知的推送策略          │     │
│ └─────────────────────────────────┘     │
├──────────────────────────────────────────┤
│ ┌ 当前配置 ───────────────────────┐     │
│ │ 语言         中文 (简体)         │     │
│ │ 主题         跟随系统            │     │
│ │ 字体大小     标准                │     │
│ │ 字体颜色     系统默认            │     │
│ │ 推送频率     实时推送            │     │
│ └─────────────────────────────────┘     │
└──────────────────────────────────────────┘

在这里插入图片描述

四、Select 组件的最佳实践

4.1 selected 和 value 的同步

Select 的 selectedvalue 都依赖于同一个 @State 变量(选中索引)。在 onSelect 回调中更新 @State 时,两者会自动同步。

保持两者同步的最简模式:

@State idx: number = 0;
private opts: SelectOption[] = [{ value: 'A' }, { value: 'B' }];

build() {
  Select(this.opts)
    .selected(this.idx)
    .value(this.opts[this.idx].value)
    .onSelect((index: number) => { this.idx = index; })
}

直接通过索引访问 opts[this.idx].value 设置按钮文本——不需要单独的 getter 方法或三元表达式。

4.2 选项样式的"命名约定"

Select 的样式方法命名有清晰的规律:

前缀 含义 对应方法
无前缀 按钮自身(折叠时) font(), fontColor()
option 选项通用 optionFont(), optionFontColor(), optionBgColor()
selectedOption 已选中选项 selectedOptionFont(), selectedOptionFontColor(), selectedOptionBgColor()

记住这个规律后,选中样式只需要在通用样式前加 selected 前缀即可。

4.3 在设置页中使用 Select 的布局模式

设置页是 Select 最常见的应用场景。推荐的布局模式:

Row() {
  Column() {           // 左侧:标签 + 说明
    Text('设置项名称')
      .fontSize(15).fontWeight(FontWeight.Bold)
    Text('一句话说明')
      .fontSize(11).fontColor('#9999AA')
  }
  .layoutWeight(1)

  Select([...])        // 右侧:选择器
    .selected(idx)
    .value(text)
    ...
}
.width('100%')
.padding({ top: 14, bottom: 14 })

这个布局确保了:

  • 左侧内容自动占据剩余空间(layoutWeight(1)
  • Select 宽度自适应其内容(选项文本的宽度)
  • 在左右之间形成自然的信息层次

4.4 Select 和 TextPicker 的选择

Select 和 TextPicker 都是"从选项中选择"的组件,但应用场景完全不同:

特性 Select TextPicker
交互方式 点击弹出下拉菜单 内联滚轮选择
选项数量 2~8 个 不限
屏幕占用 按钮高度,菜单弹出时覆盖 占用固定高度(滚轮区域)
适用场景 设置页、筛选、排序 地区选择、身高体重、长列表
视觉风格 表单/设置风格 独立选择器风格

选择规则:选项少(2~8 个)、需要节省空间、在表单/设置中的场景 → Select。选项多(>10 个)、需要滚动浏览、独立的选择页场景 → TextPicker。

4.5 性能注意事项

Select 在以下场景中表现良好:

  • 多个 Select 并存:Demo 中有 5 个 Select 实例,每个独立管理自己的状态和菜单,互不干扰
  • 选项更新:@State 索引变化时,Select 重新渲染按钮文本和选中标记,菜单在下次点击时才重新构建
  • 样式配置:样式方法在组件创建时应用,选项切换时不会重新应用样式

如果在 ForEach 等动态列表中嵌入 Select,确保 key 函数正确,避免 Select 实例在不相关的数据变化时被重新创建。

五、完整代码结构

SelectPage (~240 行)
├── 状态变量
│   ├── @State languageIdx: number — 语言选择索引
│   ├── @State themeIdx: number — 主题选择索引
│   ├── @State fontSizeIdx: number — 字体大小选择索引
│   ├── @State fontColorIdx: number — 字体颜色选择索引
│   └── @State notifyIdx: number — 通知频率选择索引
├── 选项数据
│   ├── private langOptions
│   ├── private themeOptions
│   ├── private fontSizeOptions
│   ├── private fontColorOptions
│   └── private notifyOptions
├── 辅助方法 — langText(), themeText() 等 5 个
├── 视图
│   ├── 标题栏 — ⚙️ 偏好设置
│   ├── 说明卡片 — Select 组件介绍
│   ├── 外观设置组(4 个 Select)
│   ├── 通知设置组(1 个 Select)
│   └── 当前配置摘要
└── @Builder
    ├── settingRow() — 设置行封装
    └── summaryRow() — 配置摘要行

六、总结

本文通过一个个人偏好设置 Demo 深入讲解了 HarmonyOS 中的 Select 下拉选择器组件。Select 将传统的手动组合(Text + 箭头 + 弹出菜单 + 状态管理)封装为一个完整的声明式组件,通过 selected/value/onSelect 三个方法完成核心的选中状态管理,通过丰富的样式方法(font/optionFont/selectedOptionFont 等)实现灵活的外观定制。

核心要点回顾:

  1. Select 是"选项 → 选中"的完整封装:选项数组定义 + selected 表示当前选中 + onSelect 处理变更 + value 控制按钮文本。整个流程在一个组件内完成,无需手动管理菜单的显示/隐藏。

  2. 三种样式层级:按钮样式(font/fontColor)、通用选项样式(optionFont/optionFontColor)、已选选项样式(selectedOptionFont/selectedOptionFontColor)。每个层级独立配置,满足不同设计需求。

  3. selected 和 value 的关系selected 控制菜单中的选中标记,value 控制按钮上的显示文本。两者绑定同一 @State 变量,在 onSelect 中同步更新。

  4. Select 适合选项少的场景:2~8 个选项时 Select 表现最佳。>10 个选项考虑使用 TextPicker。

  5. 在设置页中的推荐布局:左侧(标签 + 说明)+ 右侧(Select),通过 layoutWeight(1)layoutWeight(1) 让左侧占据主要空间,Select 自适应内容宽度。

Select 是 ArkUI 中"一次性选择"交互的标准化组件——无论是设置页、筛选栏还是表单选择,都可以用一行声明替代手动布局的十几行代码。理解 Select 的三层样式体系(按钮/选项/已选选项)和 selected/value 的协同工作方式,是高效使用 Select 的关键。

七、扩展思考

Select 解决了基础的下拉选择需求,但在实际项目中,选择器还有更多变化:

带图标的选择:SelectOption 支持 icon 字段,可以为每个选项添加前置图标。语言选择中可以展示国旗图标,主题选择中展示色块图标,增强选项的可识别性。

Select 和 Menu 的区别:Select 是有状态的(有"当前选中"的概念),Menu 是无状态的(点击执行操作后消失)。Select 适合"从选项中选一个"的场景,Menu 适合"执行某个操作"的场景。

与后端配置同步:真实设置页中,Select 的选中值需要持久化。在 onSelect 回调中除更新 @State 外,还需将值写入 Preferences 或发送到后端。下次进入页面时,从持久化存储读取初始值。

Select 的局限性:Select 不支持多选(Multi-select)、不支持搜索过滤(Searchable select)、不支持自定义菜单项布局(如选项前加头像)。这些高级场景需要使用自定义容器。

Select 与 TextPicker 的混合使用:某些设置页中,选项少的设置用 Select(如语言、主题),选项多的设置用 TextPicker(如城市选择)。两个组件在同一个页面中配合使用,为用户提供最适合每种场景的选择方式。

理解 Select 的定位——轻量级下拉选项组件——是正确使用它的关键。它不是万能选择器(不能多选、不能搜索),但正是这种专注让它成为了设置页和表单中最自然、最高效的单一选项交互方案。

通过本文的 Demo——个人偏好设置页面,你将 5 个 Select 组件组织在一个完整的设置页中,体验了分组布局、样式统一和实时摘要的标准模式。这个页面可以扩展为任何 App 设置中心的起点模板。

Logo

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

更多推荐