鸿蒙新特性——Select 下拉选择器详解
一、引言
在移动端应用中,选择是仅次于输入的交互方式。无论是设置页中切换语言和主题、筛选页中选择分类和排序方式、还是表单中选择性别和地区——用户不需要输入新内容,只需要从预设的选项中"选中一个"。这类场景中,最好的 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;
})
使用模式的关键:
selected(index)和value(text)绑定到同一个 @State 变量onSelect回调中更新 @State,触发selected和value同步更新- 如果选项数量较多,用辅助方法替代三元表达式:
value(this.getText())
2.4 为什么需要 selected 和 value 两个参数
Select 有两个看似重复的方法,但它们各司其职:
selected(index):控制打开菜单时哪个选项显示为选中状态(高亮/打勾)value(text):控制 Select 按钮折叠时显示的文本
两者默认使用选项对应文本,但在某些场景下需要不同——例如按钮上显示简短文本,选项菜单中显示完整标签。大多数情况下两者保持一致即可。
2.5 选项样式的渲染层级
Select 的样式方法有三层:
- 通用选项样式(
optionFont、optionFontColor)——应用于全部选项 - 已选中选项样式(
selectedOptionFont、selectedOptionFontColor)——仅应用于当前选中的选项,覆盖通用样式 - 按钮自身样式(
font、fontColor)——应用于 Select 折叠时的按钮文本,与选项样式独立
这意味着你可以让下拉菜单中的选中项用蓝色标注,同时按钮上的文本保持黑色——两者完全独立。
三、Demo 设计:个人偏好设置
3.1 功能概述
Demo 是一个个人偏好设置页面,模拟 App 设置中心的外观和交互:
- 外观设置组:4 个 Select——语言、主题、字体大小、字体颜色
- 通知设置组:1 个 Select——推送频率
- 当前配置摘要:实时展示 5 个选择器的当前选中值
- 分组卡片布局:每组设有标题和说明,模拟真实设置页的视觉结构
所有 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 的 selected 和 value 都依赖于同一个 @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 等)实现灵活的外观定制。
核心要点回顾:
-
Select 是"选项 → 选中"的完整封装:选项数组定义 + selected 表示当前选中 + onSelect 处理变更 + value 控制按钮文本。整个流程在一个组件内完成,无需手动管理菜单的显示/隐藏。
-
三种样式层级:按钮样式(
font/fontColor)、通用选项样式(optionFont/optionFontColor)、已选选项样式(selectedOptionFont/selectedOptionFontColor)。每个层级独立配置,满足不同设计需求。 -
selected 和 value 的关系:
selected控制菜单中的选中标记,value控制按钮上的显示文本。两者绑定同一 @State 变量,在 onSelect 中同步更新。 -
Select 适合选项少的场景:2~8 个选项时 Select 表现最佳。>10 个选项考虑使用 TextPicker。
-
在设置页中的推荐布局:左侧(标签 + 说明)+ 右侧(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 设置中心的起点模板。
更多推荐




所有评论(0)