HdsSideBar:打造原生级侧边栏体验 #跟着淼哥学鸿蒙
摘要: HarmonyOS 6 API 20引入的HdsSideBar组件为移动应用提供了高效的原生侧边栏解决方案。相比自定义实现,它内置手势支持、动画效果和屏幕适配,显著降低开发时间(从2-3天缩短至1小时),同时确保设计一致性。通过代码示例展示了基础配置方法,包括状态管理、内容构建和样式定义,并演示了在日记应用中的实战应用,如导航布局和数据操作。HdsSideBar的开箱即用特性使其成为鸿蒙应
·
📝 文章概述
在移动应用中,侧边栏(Sidebar/Drawer)是一个常见的导航模式。HarmonyOS 6 API 20引入的HdsSideBar组件提供了符合鸿蒙设计语言的原生侧边栏实现,开箱即用,无需从零开发。本文将深入探讨HdsSideBar的使用方法和最佳实践。
🎯 为什么选择HdsSideBar?
传统方案 vs HdsSideBar
对比表
| 特性 | 自定义实现 | HdsSideBar | 优势 |
|---|---|---|---|
| 开发时间 | 2-3天 | 1小时 | ⭐⭐⭐⭐⭐ |
| 动画效果 | 需手动实现 | 内置 | ⭐⭐⭐⭐⭐ |
| 手势支持 | 需手动处理 | 内置 | ⭐⭐⭐⭐⭐ |
| 屏幕适配 | 需测试多种设备 | 自动适配 | ⭐⭐⭐⭐⭐ |
| 设计一致性 | 难以保证 | 符合规范 | ⭐⭐⭐⭐⭐ |
| 维护成本 | 高 | 低 | ⭐⭐⭐⭐⭐ |
🚀 快速开始
第一步:导入组件
import { HdsSideBar } from '@kit.UIDesignKit' // API 20
import { SideBarContainerType } from '@ohos.arkui.advanced.SideBarContainer'
第二步:基础配置
@Entry
@ComponentV2
struct BasicSideBarDemo {
// 🔥 侧边栏状态管理
@Local isShowSidebar: boolean = false
@Local isSideBarContainerMask: boolean = true // 是否显示遮罩
@Local isAutoHide: boolean = false // 是否自动隐藏
@Local blankHeight: number = 48 // 顶部空白高度
// 🔥 侧边栏内容构建器
@Builder
SideBarPanelBuilder() {
Column() {
Text('侧边栏内容')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin(20)
Button('菜单项1')
.width('90%')
.margin(10)
Button('菜单项2')
.width('90%')
.margin(10)
Button('菜单项3')
.width('90%')
.margin(10)
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
}
// 🔥 主内容构建器
@Builder
ContentPanelBuilder() {
Column() {
// 顶部导航栏
Row() {
Button('☰')
.onClick(() => {
this.isShowSidebar = !this.isShowSidebar
})
Text('主页面')
.fontSize(20)
.layoutWeight(1)
.textAlign(TextAlign.Center)
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor('#3498DB')
// 主内容区
Text('这里是主要内容区域')
.fontSize(16)
.margin(20)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F6FA')
}
build() {
// 🔥 使用HdsSideBar组件
HdsSideBar({
sideBarPanelBuilder: () => this.SideBarPanelBuilder(),
contentPanelBuilder: () => this.ContentPanelBuilder(),
autoHide: this.isAutoHide,
contentAreaMask: this.isSideBarContainerMask,
sideBarContainerType: SideBarContainerType.Overlay,
isShowSideBar: this.isShowSidebar,
$isShowSideBar: (isShowSidebar: boolean) => {
this.isShowSidebar = !isShowSidebar
}
})
}
}
💡 日记应用实战
完整实现
@Entry
@ComponentV2
struct DiaryApp {
// 状态管理
@Local isShowSidebar: boolean = false
@Local isSideBarContainerMask: boolean = true
@Local blankHeight: number = 48
@Local isAutoHide: boolean = false
@Local diaryList: DiaryRecord[] = []
// 🔥 侧边栏构建器(导航和数据管理)
@Builder
SideBarPanelBuilder() {
Column() {
// 顶部空白(状态栏高度)
Blank().height(this.blankHeight)
// 应用标题和图标
Column() {
Text('📔')
.fontSize(48)
.margin({ bottom: 16 })
Text('极速日记')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
.margin({ bottom: 8 })
Text('记录生活的美好时光')
.fontSize(14)
.fontColor('#7F8C8D')
}
.width('100%')
.alignItems(HorizontalAlign.Center)
.margin({ bottom: 30 })
// 数据管理区域
Column() {
Text('数据管理')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#2C3E50')
.margin({ bottom: 16 })
.alignSelf(ItemAlign.Start)
// 导入按钮
Button() {
Row() {
Text('📥')
.fontSize(18)
.margin({ right: 8 })
Text('导入数据')
.fontSize(14)
.fontColor('#27AE60')
.layoutWeight(1)
}
.width('100%')
.justifyContent(FlexAlign.Start)
}
.backgroundColor('#E8F5E8')
.borderRadius(12)
.height(44)
.width('100%')
.margin({ bottom: 12 })
.onClick(() => {
this.importData()
this.isShowSidebar = false // 操作后关闭侧边栏
})
// 导出按钮
Button() {
Row() {
Text('📤')
.fontSize(18)
.margin({ right: 8 })
Text('导出数据')
.fontSize(14)
.fontColor('#3498DB')
.layoutWeight(1)
}
.width('100%')
.justifyContent(FlexAlign.Start)
}
.backgroundColor('#EBF3FD')
.borderRadius(12)
.height(44)
.width('100%')
.onClick(() => {
this.exportData()
this.isShowSidebar = false
})
}
.width('100%')
.padding({ left: 20, right: 20 })
Blank() // 撑开空间
// 底部信息
Column() {
Text(`共 ${this.diaryList.length} 篇日记`)
.fontSize(14)
.fontColor('#95A5A6')
.margin({ bottom: 4 })
Text('极速日记 v1.0')
.fontSize(12)
.fontColor('#BDC3C7')
}
.alignItems(HorizontalAlign.Center)
.padding({ bottom: 20 })
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
}
// 🔥 主内容区构建器
@Builder
ContentPanelBuilder() {
Column() {
// 顶部空白
Blank().height(this.blankHeight)
// 顶部标题栏
Row() {
// 侧边栏控制按钮
Button() {
SymbolGlyph(
this.isShowSidebar ?
$r('sys.symbol.open_sidebar') :
$r('sys.symbol.close_sidebar')
)
.fontWeight(FontWeight.Normal)
.fontSize(20)
}
.backgroundColor(Color.Transparent)
.height(40)
.width(40)
.margin({ right: 12 })
.onClick(() => {
this.isShowSidebar = !this.isShowSidebar
})
Text('极速日记')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
Blank()
// 笔记本图标(右上角)
Button('📒')
.fontSize(24)
.backgroundColor('#F8F9FA')
.borderRadius(8)
.height(40)
.width(40)
.onClick(() => {
// 跳转到写日记页面
router.pushUrl({ url: 'pages/WriteDiaryPage' })
})
}
.width('100%')
.padding({ left: 20, right: 20, top: 10, bottom: 10 })
// 主内容区(日记列表等)
this.DiaryListBuilder()
}
.width('100%')
.height('100%')
.backgroundColor('#F5F6FA')
}
// 日记列表构建器
@Builder
DiaryListBuilder() {
// ... 日记列表实现 ...
}
build() {
HdsSideBar({
sideBarPanelBuilder: (): void => this.SideBarPanelBuilder(),
contentPanelBuilder: (): void => this.ContentPanelBuilder(),
autoHide: this.isAutoHide,
contentAreaMask: this.isSideBarContainerMask,
sideBarContainerType: SideBarContainerType.Overlay,
isShowSideBar: this.isShowSidebar,
$isShowSideBar: (isShowSidebar: boolean) => {
this.isShowSidebar = !isShowSidebar
}
})
}
}
🎨 HdsSideBar参数详解
核心参数
interface HdsSideBarOptions {
// 🔥 侧边栏内容构建器
sideBarPanelBuilder: () => void
// 🔥 主内容区构建器
contentPanelBuilder: () => void
// 🔥 是否显示侧边栏
isShowSideBar: boolean
// 🔥 侧边栏状态变化回调
$isShowSideBar?: (isShow: boolean) => void
// 🔥 侧边栏类型
sideBarContainerType?: SideBarContainerType
// 🔥 是否显示内容区遮罩
contentAreaMask?: boolean
// 🔥 是否自动隐藏
autoHide?: boolean
// 🔥 侧边栏宽度
sideBarWidth?: number | string
// 🔥 最小侧边栏宽度
minSideBarWidth?: number | string
// 🔥 最大侧边栏宽度
maxSideBarWidth?: number | string
}
SideBarContainerType枚举
参数配置示例
// 🔥 Overlay模式(推荐用于手机)
HdsSideBar({
sideBarPanelBuilder: () => this.SideBarBuilder(),
contentPanelBuilder: () => this.ContentBuilder(),
isShowSideBar: this.isShowSidebar,
sideBarContainerType: SideBarContainerType.Overlay, // 悬浮式
contentAreaMask: true, // 显示遮罩
autoHide: false, // 不自动隐藏
sideBarWidth: '80%', // 侧边栏宽度
minSideBarWidth: 200, // 最小宽度
maxSideBarWidth: 300 // 最大宽度
})
// 🔥 Embed模式(推荐用于平板)
HdsSideBar({
sideBarPanelBuilder: () => this.SideBarBuilder(),
contentPanelBuilder: () => this.ContentBuilder(),
isShowSideBar: true, // 默认显示
sideBarContainerType: SideBarContainerType.Embed, // 嵌入式
contentAreaMask: false, // 不显示遮罩
autoHide: false,
sideBarWidth: 300 // 固定宽度
})
🔧 高级技巧
1. 响应式设计
@ComponentV2
struct ResponsiveSideBar {
@Local isShowSidebar: boolean = false
@Local sideBarType: SideBarContainerType = SideBarContainerType.AUTO
@Local deviceType: string = 'phone'
aboutToAppear() {
// 检测设备类型
this.deviceType = this.getDeviceType()
// 根据设备类型设置侧边栏模式
if (this.deviceType === 'tablet') {
this.sideBarType = SideBarContainerType.Embed
this.isShowSidebar = true // 平板默认显示
} else {
this.sideBarType = SideBarContainerType.Overlay
this.isShowSidebar = false // 手机默认隐藏
}
}
getDeviceType(): string {
// 根据屏幕宽度判断设备类型
const screenWidth = display.getDefaultDisplaySync().width
return screenWidth > 600 ? 'tablet' : 'phone'
}
build() {
HdsSideBar({
sideBarPanelBuilder: () => this.SideBarBuilder(),
contentPanelBuilder: () => this.ContentBuilder(),
isShowSideBar: this.isShowSidebar,
sideBarContainerType: this.sideBarType,
$isShowSideBar: (isShow) => {
this.isShowSidebar = !isShow
}
})
}
}
2. 手势增强
@Builder
ContentWithGesture() {
Column() {
// 主内容
// ...
}
.gesture(
// 从左边缘向右滑动打开侧边栏
PanGesture({ direction: PanDirection.Right })
.onActionStart((event: GestureEvent) => {
// 检查是否从左边缘开始
if (event.fingerList[0].localX < 50) {
this.isShowSidebar = true
}
})
)
.gesture(
// 向左滑动关闭侧边栏
PanGesture({ direction: PanDirection.Left })
.onActionStart(() => {
if (this.isShowSidebar) {
this.isShowSidebar = false
}
})
)
}
3. 动画定制
@ComponentV2
struct AnimatedSideBar {
@Local isShowSidebar: boolean = false
@Local sideBarOpacity: number = 0
@Local sideBarTranslateX: number = -300
// 监听侧边栏状态变化
@Watch('onSideBarChange')
@Local isShowSideBar: boolean = false
onSideBarChange() {
// 自定义动画
animateTo(
{
duration: 300,
curve: Curve.EaseInOut
},
() => {
if (this.isShowSideBar) {
this.sideBarOpacity = 1
this.sideBarTranslateX = 0
} else {
this.sideBarOpacity = 0
this.sideBarTranslateX = -300
}
}
)
}
@Builder
CustomSideBar() {
Column() {
// 侧边栏内容
}
.opacity(this.sideBarOpacity)
.translate({ x: this.sideBarTranslateX })
}
}
4. 嵌套导航
@ComponentV2
struct NestedNavSideBar {
@Local currentSection: string = 'home'
@Local isShowSidebar: boolean = false
@Builder
SideBarWithNav() {
Column() {
// 导航菜单
List() {
ListItem() {
this.NavItemBuilder('home', '首页', '🏠')
}
.onClick(() => {
this.currentSection = 'home'
this.isShowSidebar = false
})
ListItem() {
this.NavItemBuilder('favorite', '收藏', '⭐')
}
.onClick(() => {
this.currentSection = 'favorite'
this.isShowSidebar = false
})
ListItem() {
this.NavItemBuilder('settings', '设置', '⚙️')
}
.onClick(() => {
this.currentSection = 'settings'
this.isShowSidebar = false
})
}
}
}
@Builder
NavItemBuilder(id: string, title: string, icon: string) {
Row() {
Text(icon)
.fontSize(20)
.margin({ right: 12 })
Text(title)
.fontSize(16)
.fontColor(this.currentSection === id ? '#3498DB' : '#2C3E50')
.fontWeight(this.currentSection === id ? FontWeight.Bold : FontWeight.Normal)
Blank()
if (this.currentSection === id) {
Text('▶')
.fontSize(12)
.fontColor('#3498DB')
}
}
.width('100%')
.height(50)
.padding({ left: 20, right: 20 })
.backgroundColor(this.currentSection === id ? '#EBF3FD' : Color.Transparent)
}
@Builder
ContentBySection() {
if (this.currentSection === 'home') {
this.HomeContent()
} else if (this.currentSection === 'favorite') {
this.FavoriteContent()
} else if (this.currentSection === 'settings') {
this.SettingsContent()
}
}
build() {
HdsSideBar({
sideBarPanelBuilder: () => this.SideBarWithNav(),
contentPanelBuilder: () => this.ContentBySection(),
isShowSideBar: this.isShowSidebar,
sideBarContainerType: SideBarContainerType.Overlay,
$isShowSideBar: (isShow) => {
this.isShowSidebar = !isShow
}
})
}
}
📊 设计最佳实践
UI设计建议
mindmap
root((侧边栏设计))
宽度
手机: 80%屏宽
平板: 280-320px固定
最小宽度: 200px
最大宽度: 400px
高度
全屏高度
预留状态栏
底部安全区
内容
顶部: Logo+标题
中间: 导航菜单
底部: 版本信息
交互
点击遮罩关闭
滑动手势
动画流畅
颜色
背景: 白色/浅灰
文字: 深灰
高亮: 主题色
布局建议
| 区域 | 高度 | 内容 | 说明 |
|---|---|---|---|
| 顶部区 | 100-150px | Logo、标题、描述 | 品牌展示 |
| 导航区 | 自适应 | 菜单项列表 | 主要功能入口 |
| 操作区 | 100-200px | 快捷操作按钮 | 次要功能 |
| 底部区 | 60-80px | 版本、统计信息 | 辅助信息 |
🎓 最佳实践
✅ 推荐做法
-
合理使用模式
- 手机:Overlay模式 + 点击遮罩关闭
- 平板:Embed模式 + 默认显示
-
状态管理
@Local isShowSidebar: boolean = false // 使用@Local管理状态 -
用户体验
- 提供明显的打开/关闭按钮
- 支持边缘滑动手势
- 操作后自动关闭侧边栏
-
性能优化
- 避免在侧边栏中加载大量数据
- 使用懒加载加载图片
- 避免复杂动画
❌ 避免做法
- ❌ 侧边栏宽度超过90%屏宽(影响主内容可见性)
- ❌ 不提供关闭按钮(用户体验差)
- ❌ 侧边栏内容过多(滚动体验差)
- ❌ 不适配不同屏幕尺寸
- ❌ 动画时间过长(>500ms)
📚 总结
HdsSideBar为HarmonyOS应用提供了:
✅ 开箱即用:无需从零实现,节省开发时间
✅ 原生体验:符合鸿蒙设计语言,交互流畅
✅ 自动适配:智能适配不同屏幕尺寸
✅ 功能完善:支持遮罩、手势、动画等
✅ 易于定制:灵活的Builder模式,自定义内容
核心要点
- 选择合适的模式:Overlay(手机)vs Embed(平板)
- 响应式设计:根据设备类型自动调整
- 用户体验优先:提供多种打开/关闭方式
- 性能优化:避免复杂内容和动画
更多推荐



所有评论(0)