鸿蒙自定义Menu下拉选择组件开发实战
·
鸿蒙自定义下拉选择组件开发指南
为什么要做这个
今天在开发鸿蒙应用时,我遇到了一个棘手的样式定制问题:官方提供的 Select 组件 虽然稳定性和易用性都很出色,但在实际开发中存在限制:
官方文档中部分样式属性需要在 API 20 及以上版本才能使用,而我的项目需要兼容更低版本,导致样式定制需求完全无法满足。
为解决这个问题,我翻遍了鸿蒙官方文档,却始终没找到适配低版本的解决方案;上网搜索时,相关内容要么是收费教程,要么是基于旧版 API 的过时文章。
最终,我决定自己 “手搓” 一个自定义 Select 组件,虽然组件功能和细节可能还有些粗糙,但已能满足基础使用需求。下面我会记录该组件的完整使用教程,也欢迎大家在评论区交流优化思路。如果这份内容能够帮助到你,非常荣幸。
原生鸿蒙<select>组件存在两个限制:
- 无法自定义下拉箭头图标
- 无法灵活控制文本样式
- 箭头图标固定:下拉箭头是系统自带的,没法替换成设计稿里的自定义图标,视觉统一性差;
通过自定义组件MenuBuilde 完美解决这些问题,实现效果如下:
修改前原生组件:
修改后效果:
核心代码解析
@Preview
@Component
export struct MenuBuilder {
// 关键属性定义
@Prop img0: string = '' // 默认箭头图标路径
@Prop img1: string = '' // 激活状态箭头图标路径
@Prop menuItermList: string[] = [''] // 选项数据源
@Prop selectedValue: string = '请选择' // 默认提示文本
// 状态管理
@State private isMenuVisible: boolean = false
@State private ischoose: boolean = false
@State selectedID: number = -1
// 回调函数
private returnToSelection?: (value: string) => void
build() {
Row({ space: 10 }) {
// 动态文本样式
Text(this.selectedValue)
.fontColor(!this.ischoose ? '#dcdcdc' : Color.Black)
// 动态切换箭头图标
if (this.ischoose) {
Image(this.isMenuVisible ? $r(this.img0) : $r(this.img1))
.size({ width: '20vp', height: '20vp' })
.fillColor(Color.Black) // 已选择状态着色
} else {
Image(this.isMenuVisible ? $r(this.img0) : $r(this.img1))
.size({ width: '20vp', height: '20vp' })
}
}
// 菜单绑定与事件处理
.bindMenu(this.MenuBuilder(this.menuItermList), {
onAppear: () => this.isMenuVisible = true,
onDisappear: () => this.isMenuVisible = false
})
.onClick(() => this.isMenuVisible = !this.isMenuVisible)
}
// 菜单构建器
@Builder
MenuBuilder(itemList: string[]) {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
ForEach(itemList, (item: string, index) => {
Row() {
Text(item)
.layoutConfig({ width: '100%', height: 32 })
.textStyle({
weight: 400,
size: 14,
color: Color.Black,
align: TextAlign.Center
})
}
// 选中项高亮
.backgroundColor(this.selectedID == index ? '#f1f4f9' : Color.White)
.borderRadius(10)
.onClick(() => {
this.selectedID = index
this.ischoose = true
this.selectedValue = item
this.returnToSelection?.(item) // 执行回调
})
// 添加分隔线
if (index != itemList.length - 1) {
Divider().height(10).width('80%').color('#ccc')
}
})
}
.width(100) // 菜单宽度控制
}
}
四大核心功能
-
动态图标系统
- 通过
img0/img1属性注入自定义图标资源 - 根据
isMenuVisible状态自动切换图标
示例代码:
Image(this.isMenuVisible ? $r(this.img0) : $r(this.img1)) - 通过
-
智能状态管理
ischoose标记选择状态selectedID记录当前选中项索引isMenuVisible控制菜单显隐
-
视觉反馈优化
- 未选择时文本置灰 (
#dcdcdc) - 选中项背景高亮 (
#f1f4f9) - 图标点击后黑色填充 (
fillColor)
- 未选择时文本置灰 (
-
数据驱动架构
- 通过
menuItermList传入选项数组 - 使用
ForEach动态生成菜单项
- 通过
使用教程
步骤1:导入组件
import { MenuBuilde } from './MenuBuilde'
步骤2:配置参数
@Entry
@Component
struct Example {
@State currentVal: string = "苹果"
private options: string[] = ["苹果", "香蕉", "橙子"]
build() {
Column() {
MenuBuilde({
img0: "app.media.arrow_up", // 自定义向上箭头
img1: "app.media.arrow_down",// 自定义向下箭头
menuItermList: this.options,
selectedValue: this.currentVal
})
.onSelectionChange((val) => this.currentVal = val) // 回调绑定
}
}
}
参数说明表
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
img0 |
string | 是 | 默认状态图标路径 |
img1 |
string | 是 | 激活状态图标路径 |
menuItermList |
string[] | 是 | 选项文本数组 |
selectedValue |
string | 否 | 初始选中项(默认"请选择") |
扩展建议
-
增加图标动画
在onClick中添加旋转动画:.onClick(() => { animateTo({ duration: 200 }, () => { this.rotateAngle = this.isMenuVisible ? 180 : 0 }) }) -
支持图标尺寸参数化
新增@Prop iconSize: number = 20属性
替换写死的20vp尺寸值 -
多语言适配
将selectedValue绑定资源文件:@Prop selectedValue: Resource = $r('app.string.please_select')
效果对比
| 特性 | 原生Select | 自定义MenuBuilde |
|---|---|---|
| 图标自定义 | ❌ | ✔️ |
| 文本样式控制 | ❌ | ✔️ |
| 选中态反馈 | 基础实现 | 高亮动画 |
| 回调灵活性 | 有限 | 完全可控 |
源码获取:gitee实际项目中打开即可使用
更多推荐


所有评论(0)