
132.[HarmonyOS NEXT 实战案例五:SideBarContainer] 侧边栏容器实战:悬浮模式侧边栏(Overlay)进阶篇 原创
[HarmonyOS NEXT 实战案例五:SideBarContainer] 侧边栏容器实战:悬浮模式侧边栏(Overlay)进阶篇
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
一、状态管理进阶
在基础篇中,我们已经实现了移动端抽屉菜单的基本布局和功能。在本篇教程中,我们将深入探讨如何通过状态管理和交互功能增强,使侧边栏更加智能和易用。
1.1 状态变量设计
首先,让我们扩展状态变量,以支持更丰富的功能:
@Entry
@Component
struct MobileMenu {
// 侧边栏显示状态
@State isSideBarShow: boolean = false
// 当前选中的菜单项索引
@State currentIndex: number = 0
// 动画状态
@State animationState: AnimationStatus = AnimationStatus.Initial
// 用户信息
@State userInfo: UserInfo = {
name: '用户名',
email: '[email protected]',
avatar: $r('app.media.avatar'),
isLoggedIn: true
}
// 菜单项列表
@State menuItems: MenuItem[] = [
{ id: 0, title: '首页', icon: $r('app.media.ic_home'), badge: 0 },
{ id: 1, title: '消息', icon: $r('app.media.ic_message'), badge: 5 },
{ id: 2, title: '收藏', icon: $r('app.media.ic_favorite'), badge: 0 },
{ id: 3, title: '设置', icon: $r('app.media.ic_settings'), badge: 0 },
{ id: 4, title: '关于', icon: $r('app.media.ic_about'), badge: 0 }
]
// 主题设置
@State isDarkMode: boolean = false
// 屏幕信息
@State screenWidth: number = 0
@State screenHeight: number = 0
// 构建方法...
}
// 用户信息接口
interface UserInfo {
name: string
email: string
avatar: Resource
isLoggedIn: boolean
}
// 菜单项接口
interface MenuItem {
id: number
title: string
icon: Resource
badge: number
}
// 动画状态枚举
enum AnimationStatus {
Initial,
Playing,
Stopped
}
这些扩展的状态变量使我们能够:
- 跟踪用户信息:包括用户名、邮箱、头像和登录状态
- 管理菜单项:将菜单项抽象为对象数组,便于动态管理
- 添加徽章功能:为菜单项添加徽章,显示未读消息数量等信息
- 支持主题切换:通过
isDarkMode
状态变量支持深色模式 - 响应式设计:通过
screenWidth
和screenHeight
状态变量支持响应式布局
1.2 生命周期管理
接下来,我们添加生命周期方法,以便在组件创建和销毁时执行必要的操作:
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 屏幕变化监听器ID
private screenChangeListener: number = 0
aboutToAppear() {
// 获取屏幕尺寸
this.updateScreenSize()
// 添加屏幕变化监听器
this.screenChangeListener = window.on('windowSizeChange', () => {
this.updateScreenSize()
})
// 加载用户信息
this.loadUserInfo()
// 加载菜单项
this.loadMenuItems()
}
aboutToDisappear() {
// 移除屏幕变化监听器
if (this.screenChangeListener) {
window.off('windowSizeChange', this.screenChangeListener)
}
// 保存状态
this.saveState()
}
// 更新屏幕尺寸
private updateScreenSize() {
this.screenWidth = px2vp(window.getWindowWidth())
this.screenHeight = px2vp(window.getWindowHeight())
}
// 加载用户信息
private loadUserInfo() {
// 从持久化存储加载用户信息
// 这里使用模拟数据
this.userInfo = {
name: '张三',
email: '[email protected]',
avatar: $r('app.media.avatar'),
isLoggedIn: true
}
}
// 加载菜单项
private loadMenuItems() {
// 从持久化存储加载菜单项
// 这里使用模拟数据
this.menuItems = [
{ id: 0, title: '首页', icon: $r('app.media.ic_home'), badge: 0 },
{ id: 1, title: '消息', icon: $r('app.media.ic_message'), badge: 5 },
{ id: 2, title: '收藏', icon: $r('app.media.ic_favorite'), badge: 0 },
{ id: 3, title: '设置', icon: $r('app.media.ic_settings'), badge: 0 },
{ id: 4, title: '关于', icon: $r('app.media.ic_about'), badge: 0 }
]
}
// 保存状态
private saveState() {
// 将状态保存到持久化存储
// 这里仅作示例,实际应用中应使用AppStorage或其他存储机制
console.info('保存状态:', {
currentIndex: this.currentIndex,
isDarkMode: this.isDarkMode
})
}
// 构建方法...
}
这些生命周期方法使我们能够:
- 响应屏幕变化:监听窗口大小变化,更新屏幕尺寸状态变量
- 加载和保存状态:在组件创建时加载状态,在组件销毁时保存状态
- 资源清理:在组件销毁时移除事件监听器,避免内存泄漏
二、交互功能增强
2.1 徽章显示
为了增强菜单项的信息展示能力,我们可以添加徽章功能,显示未读消息数量等信息:
@Builder MenuItem(item: MenuItem) {
Row() {
Image(item.icon)
.width(24)
.height(24)
.margin({ right: 16 })
Text(item.title)
.fontSize(16)
.fontColor(this.currentIndex === item.id ? '#FF4081' : '#333333')
if (item.badge > 0) {
Blank()
Text(`${item.badge}`)
.fontSize(12)
.fontColor(Color.White)
.backgroundColor('#FF4081')
.borderRadius(10)
.width(item.badge > 9 ? 24 : 20)
.height(20)
.textAlign(TextAlign.Center)
}
}
.width('100%')
.padding({ left: 24, right: 24, top: 16, bottom: 16 })
.backgroundColor(this.currentIndex === item.id ? '#F5F5F5' : '#FFFFFF')
.borderRadius(8)
.onClick(() => {
this.currentIndex = item.id
this.isSideBarShow = false
})
}
这段代码实现了徽章显示功能:
- 条件渲染:只有当
badge
值大于0时才显示徽章 - 自适应宽度:根据徽章数值调整宽度,确保显示效果良好
- 样式设置:使用圆角和背景色使徽章更加醒目
2.2 手势增强
为了提供更自然的交互体验,我们可以增强手势控制:
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 手势相关状态
@State dragOffset: number = 0
@State isDragging: boolean = false
build() {
Stack() {
SideBarContainer(SideBarContainerType.Overlay) {
// 侧边栏内容...
// 主内容区
Stack() {
this.MainContent()
// 左侧手势区域
Column()
.width(20)
.height('100%')
.position({ x: 0, y: 0 })
.gesture(
PanGesture({ direction: PanDirection.Horizontal })
.onActionStart(() => {
this.isDragging = true
this.dragOffset = 0
})
.onActionUpdate((event: GestureEvent) => {
if (event.offsetX > 0) {
this.dragOffset = Math.min(event.offsetX, 280)
}
})
.onActionEnd(() => {
this.isDragging = false
if (this.dragOffset > 140) {
this.isSideBarShow = true
}
this.dragOffset = 0
})
)
// 拖动时的预览
if (this.isDragging && this.dragOffset > 0) {
Column() {
// 侧边栏预览内容
this.SideBarPreview()
}
.width(this.dragOffset)
.height('100%')
.position({ x: 0, y: 0 })
.backgroundColor('#FFFFFF')
}
}
}
.showSideBar(this.isSideBarShow)
.sideBarWidth(280)
.minSideBarWidth(280)
.maxSideBarWidth(280)
.onChange((isShow: boolean) => {
this.isSideBarShow = isShow
})
// 遮罩层
if (this.isSideBarShow) {
Column()
.width('100%')
.height('100%')
.backgroundColor('#000000')
.opacity(0.5)
.onClick(() => {
this.isSideBarShow = false
})
}
}
.width('100%')
.height('100%')
}
// 侧边栏预览
@Builder SideBarPreview() {
Column() {
// 简化版的侧边栏内容
Image(this.userInfo.avatar)
.width(40)
.height(40)
.borderRadius(20)
.margin({ top: 60, bottom: 20 })
ForEach(this.menuItems, (item: MenuItem) => {
Row() {
Image(item.icon)
.width(24)
.height(24)
}
.width('100%')
.padding(16)
})
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center)
}
// 其他构建器...
}
这段代码实现了增强的手势控制:
- 拖动状态跟踪:使用
isDragging
和dragOffset
状态变量跟踪拖动状态和偏移量 - 渐进式显示:根据拖动偏移量显示侧边栏预览
- 阈值判断:当拖动偏移量超过一定阈值(这里是140像素)时,显示完整侧边栏
- 预览内容:在拖动过程中显示简化版的侧边栏内容,提供视觉反馈
2.3 主题切换
为了支持深色模式,我们可以添加主题切换功能:
@Entry
@Component
struct MobileMenu {
// 状态变量...
build() {
Stack() {
SideBarContainer(SideBarContainerType.Overlay) {
// 侧边栏内容...
// 主内容区...
}
// SideBarContainer配置...
}
.width('100%')
.height('100%')
.backgroundColor(this.isDarkMode ? '#121212' : '#FFFFFF')
}
// 切换主题
private toggleTheme() {
this.isDarkMode = !this.isDarkMode
// 应用主题变化
this.applyTheme()
}
// 应用主题
private applyTheme() {
// 这里可以更新全局主题或应用特定样式
console.info('应用主题:', this.isDarkMode ? '深色模式' : '浅色模式')
}
// 构建器...
}
在侧边栏内容中,我们可以添加主题切换开关:
@Builder SideBarContent() {
Column() {
// 用户信息区域...
// 菜单选项...
// 主题切换
Row() {
Text('深色模式')
.fontSize(16)
.fontColor(this.isDarkMode ? '#FFFFFF' : '#333333')
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.isDarkMode })
.onChange((isOn: boolean) => {
this.isDarkMode = isOn
this.applyTheme()
})
}
.width('100%')
.padding({ left: 24, right: 24, top: 16, bottom: 16 })
.margin({ top: 40 })
// 底部退出按钮...
}
.width('100%')
.height('100%')
.backgroundColor(this.isDarkMode ? '#121212' : '#FFFFFF')
}
这段代码实现了主题切换功能:
- 主题状态:使用
isDarkMode
状态变量跟踪当前主题 - 切换控件:使用
Toggle
组件提供直观的切换界面 - 应用主题:在主题变化时调用
applyTheme
方法应用主题变化 - 自适应样式:根据当前主题调整文本颜色和背景颜色
三、状态持久化
为了在应用重启后恢复用户的设置和状态,我们需要实现状态持久化:
3.1 使用AppStorage
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 从AppStorage加载的状态
@StorageProp('isDarkMode') isDarkMode: boolean = false
@StorageProp('currentIndex') currentIndex: number = 0
aboutToAppear() {
// 初始化AppStorage
if (AppStorage.Has('isDarkMode') === false) {
AppStorage.SetOrCreate('isDarkMode', false)
}
if (AppStorage.Has('currentIndex') === false) {
AppStorage.SetOrCreate('currentIndex', 0)
}
// 其他初始化...
}
// 更新AppStorage中的状态
private updateAppStorage() {
AppStorage.Set('isDarkMode', this.isDarkMode)
AppStorage.Set('currentIndex', this.currentIndex)
}
// 构建方法...
}
3.2 使用首选项
对于需要在应用重启后保留的数据,我们可以使用首选项(Preferences):
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 首选项实例
private preferences: Preferences | null = null
aboutToAppear() {
// 获取首选项实例
this.getPreferences().then(() => {
// 加载状态
this.loadStateFromPreferences()
})
// 其他初始化...
}
aboutToDisappear() {
// 保存状态到首选项
this.saveStateToPreferences()
// 其他清理...
}
// 获取首选项实例
private async getPreferences() {
try {
this.preferences = await Preferences.getPreferences(getContext(this), 'MobileMenuPrefs')
} catch (error) {
console.error('获取首选项失败:', error)
}
}
// 从首选项加载状态
private async loadStateFromPreferences() {
if (!this.preferences) return
try {
// 加载深色模式设置
const isDarkMode = await this.preferences.get('isDarkMode', false)
this.isDarkMode = isDarkMode
// 加载当前索引
const currentIndex = await this.preferences.get('currentIndex', 0)
this.currentIndex = currentIndex
// 应用主题
this.applyTheme()
} catch (error) {
console.error('加载状态失败:', error)
}
}
// 保存状态到首选项
private async saveStateToPreferences() {
if (!this.preferences) return
try {
// 保存深色模式设置
await this.preferences.put('isDarkMode', this.isDarkMode)
// 保存当前索引
await this.preferences.put('currentIndex', this.currentIndex)
// 提交更改
await this.preferences.flush()
} catch (error) {
console.error('保存状态失败:', error)
}
}
// 构建方法...
}
这段代码实现了状态持久化:
- 使用首选项:通过
Preferences
API保存和加载状态 - 异步操作:使用
async/await
处理异步操作 - 错误处理:添加适当的错误处理,提高应用的健壮性
- 自动加载和保存:在组件创建时自动加载状态,在组件销毁时自动保存状态
四、高级交互特性
4.1 动画效果增强
为了提供更流畅的用户体验,我们可以增强动画效果:
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 动画控制器
@State sideBarAnimator: AnimatorResult | null = null
aboutToAppear() {
// 创建动画控制器
this.createAnimator()
// 其他初始化...
}
// 创建动画控制器
private createAnimator() {
// 创建侧边栏动画
this.sideBarAnimator = Animator.create({
duration: 300, // 动画持续时间,单位毫秒
curve: Curve.EaseOut, // 动画曲线
iterations: 1, // 动画重复次数
playMode: PlayMode.Normal // 动画播放模式
})
}
// 播放侧边栏显示动画
private playSideBarShowAnimation() {
if (!this.sideBarAnimator) return
// 重置动画
this.sideBarAnimator.reset()
// 设置动画属性
this.sideBarAnimator.animate({
transform: {
translate: { x: '0%', y: '0%' }
},
opacity: 1
})
// 播放动画
this.sideBarAnimator.play()
}
// 播放侧边栏隐藏动画
private playSideBarHideAnimation() {
if (!this.sideBarAnimator) return
// 重置动画
this.sideBarAnimator.reset()
// 设置动画属性
this.sideBarAnimator.animate({
transform: {
translate: { x: '-100%', y: '0%' }
},
opacity: 0
})
// 播放动画
this.sideBarAnimator.play()
}
// 构建方法...
}
4.2 自定义过渡效果
我们可以使用Transition
组件为侧边栏添加自定义过渡效果:
@Entry
@Component
struct MobileMenu {
// 状态变量...
build() {
Stack() {
SideBarContainer(SideBarContainerType.Overlay) {
// 侧边栏内容...
// 主内容区...
}
// SideBarContainer配置...
}
.width('100%')
.height('100%')
.transition({
type: TransitionType.Insert,
opacity: 0,
translate: { x: '-100%', y: '0%' }
})
.transition({
type: TransitionType.Delete,
opacity: 0,
translate: { x: '-100%', y: '0%' }
})
}
// 构建器...
}
4.3 交互反馈
为了提供更好的交互反馈,我们可以添加触觉反馈和声音效果:
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 触觉反馈控制器
private vibrationController: VibrationController = new VibrationController()
// 提供触觉反馈
private provideTactileFeedback() {
// 触发短暂振动
this.vibrationController.vibrate(50)
}
// 构建器...
@Builder MenuItem(item: MenuItem) {
Row() {
// 菜单项内容...
}
.width('100%')
.padding({ left: 24, right: 24, top: 16, bottom: 16 })
.backgroundColor(this.currentIndex === item.id ? '#F5F5F5' : '#FFFFFF')
.borderRadius(8)
.onClick(() => {
// 提供触觉反馈
this.provideTactileFeedback()
// 更新状态
this.currentIndex = item.id
this.isSideBarShow = false
})
}
}
五、实战案例:自定义菜单项
在实际应用中,我们可能需要支持不同类型的菜单项,例如带有开关、徽章或子菜单的菜单项。下面我们将实现一个支持多种类型的菜单项系统:
5.1 菜单项类型定义
// 菜单项类型枚举
enum MenuItemType {
Normal, // 普通菜单项
Switch, // 带开关的菜单项
Badge, // 带徽章的菜单项
Submenu // 带子菜单的菜单项
}
// 菜单项接口
interface MenuItem {
id: number
title: string
icon: Resource
type: MenuItemType
badge?: number
isOn?: boolean
subItems?: SubMenuItem[]
}
// 子菜单项接口
interface SubMenuItem {
id: number
title: string
icon?: Resource
}
5.2 菜单项数据
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 菜单项列表
@State menuItems: MenuItem[] = [
{
id: 0,
title: '首页',
icon: $r('app.media.ic_home'),
type: MenuItemType.Normal
},
{
id: 1,
title: '消息',
icon: $r('app.media.ic_message'),
type: MenuItemType.Badge,
badge: 5
},
{
id: 2,
title: '收藏',
icon: $r('app.media.ic_favorite'),
type: MenuItemType.Normal
},
{
id: 3,
title: '设置',
icon: $r('app.media.ic_settings'),
type: MenuItemType.Submenu,
subItems: [
{ id: 31, title: '账号设置' },
{ id: 32, title: '通知设置' },
{ id: 33, title: '隐私设置' }
]
},
{
id: 4,
title: '深色模式',
icon: $r('app.media.ic_dark_mode'),
type: MenuItemType.Switch,
isOn: false
},
{
id: 5,
title: '关于',
icon: $r('app.media.ic_about'),
type: MenuItemType.Normal
}
]
// 构建方法...
}
5.3 菜单项构建器
@Builder SideBarContent() {
Column() {
// 用户信息区域...
// 菜单选项
Column() {
ForEach(this.menuItems, (item: MenuItem) => {
this.renderMenuItem(item)
})
}
.margin({ top: 50 })
// 底部退出按钮...
}
.width('100%')
.height('100%')
.backgroundColor(this.isDarkMode ? '#121212' : '#FFFFFF')
}
// 渲染菜单项
@Builder renderMenuItem(item: MenuItem) {
if (item.type === MenuItemType.Normal) {
this.NormalMenuItem(item)
} else if (item.type === MenuItemType.Badge) {
this.BadgeMenuItem(item)
} else if (item.type === MenuItemType.Switch) {
this.SwitchMenuItem(item)
} else if (item.type === MenuItemType.Submenu) {
this.SubmenuMenuItem(item)
}
}
// 普通菜单项
@Builder NormalMenuItem(item: MenuItem) {
Row() {
Image(item.icon)
.width(24)
.height(24)
.margin({ right: 16 })
Text(item.title)
.fontSize(16)
.fontColor(this.currentIndex === item.id ? '#FF4081' : '#333333')
}
.width('100%')
.padding({ left: 24, right: 24, top: 16, bottom: 16 })
.backgroundColor(this.currentIndex === item.id ? '#F5F5F5' : '#FFFFFF')
.borderRadius(8)
.onClick(() => {
this.currentIndex = item.id
this.isSideBarShow = false
})
}
// 带徽章的菜单项
@Builder BadgeMenuItem(item: MenuItem) {
Row() {
Image(item.icon)
.width(24)
.height(24)
.margin({ right: 16 })
Text(item.title)
.fontSize(16)
.fontColor(this.currentIndex === item.id ? '#FF4081' : '#333333')
if (item.badge && item.badge > 0) {
Blank()
Text(`${item.badge}`)
.fontSize(12)
.fontColor(Color.White)
.backgroundColor('#FF4081')
.borderRadius(10)
.width(item.badge > 9 ? 24 : 20)
.height(20)
.textAlign(TextAlign.Center)
}
}
.width('100%')
.padding({ left: 24, right: 24, top: 16, bottom: 16 })
.backgroundColor(this.currentIndex === item.id ? '#F5F5F5' : '#FFFFFF')
.borderRadius(8)
.onClick(() => {
this.currentIndex = item.id
this.isSideBarShow = false
})
}
// 带开关的菜单项
@Builder SwitchMenuItem(item: MenuItem) {
Row() {
Image(item.icon)
.width(24)
.height(24)
.margin({ right: 16 })
Text(item.title)
.fontSize(16)
.fontColor('#333333')
Blank()
Toggle({ type: ToggleType.Switch, isOn: item.isOn || false })
.onChange((isOn: boolean) => {
// 更新菜单项状态
const index = this.menuItems.findIndex(i => i.id === item.id)
if (index !== -1) {
this.menuItems[index].isOn = isOn
}
// 处理特定菜单项
if (item.id === 4) { // 深色模式
this.isDarkMode = isOn
this.applyTheme()
}
})
}
.width('100%')
.padding({ left: 24, right: 24, top: 16, bottom: 16 })
.backgroundColor('#FFFFFF')
.borderRadius(8)
}
// 带子菜单的菜单项
@Builder SubmenuMenuItem(item: MenuItem) {
Column() {
// 主菜单项
Row() {
Image(item.icon)
.width(24)
.height(24)
.margin({ right: 16 })
Text(item.title)
.fontSize(16)
.fontColor(this.currentIndex === item.id ? '#FF4081' : '#333333')
Blank()
Image($r('app.media.ic_arrow_down'))
.width(16)
.height(16)
.rotate({ z: 1, angle: this.isSubmenuOpen(item.id) ? 180 : 0 })
}
.width('100%')
.padding({ left: 24, right: 24, top: 16, bottom: 16 })
.backgroundColor(this.currentIndex === item.id ? '#F5F5F5' : '#FFFFFF')
.borderRadius(8)
.onClick(() => {
this.toggleSubmenu(item.id)
})
// 子菜单项
if (this.isSubmenuOpen(item.id) && item.subItems) {
Column() {
ForEach(item.subItems, (subItem: SubMenuItem) => {
Row() {
if (subItem.icon) {
Image(subItem.icon)
.width(20)
.height(20)
.margin({ right: 12 })
}
Text(subItem.title)
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding({ left: 64, right: 24, top: 12, bottom: 12 })
.backgroundColor('#F9F9F9')
.onClick(() => {
this.handleSubItemClick(item.id, subItem.id)
})
})
}
.width('100%')
}
}
.width('100%')
}
5.4 子菜单状态管理
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 打开的子菜单ID集合
@State openSubmenus: Set<number> = new Set()
// 检查子菜单是否打开
private isSubmenuOpen(id: number): boolean {
return this.openSubmenus.has(id)
}
// 切换子菜单状态
private toggleSubmenu(id: number) {
if (this.isSubmenuOpen(id)) {
this.openSubmenus.delete(id)
} else {
this.openSubmenus.add(id)
}
// 触发UI更新
this.openSubmenus = new Set(this.openSubmenus)
}
// 处理子菜单项点击
private handleSubItemClick(parentId: number, subItemId: number) {
console.info('子菜单项点击:', { parentId, subItemId })
// 这里可以根据需要处理子菜单项点击事件
// 例如,导航到特定页面或执行特定操作
// 关闭侧边栏
this.isSideBarShow = false
}
// 构建方法...
}
六、性能优化与用户体验提升
6.1 延迟加载
为了提高应用的启动性能,我们可以使用延迟加载技术:
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 延迟加载状态
@State isContentLoaded: boolean = false
aboutToAppear() {
// 其他初始化...
// 延迟加载内容
setTimeout(() => {
this.loadMenuItems()
this.isContentLoaded = true
}, 100)
}
build() {
Stack() {
if (this.isContentLoaded) {
SideBarContainer(SideBarContainerType.Overlay) {
// 侧边栏内容...
// 主内容区...
}
// SideBarContainer配置...
} else {
// 加载指示器
Column() {
LoadingProgress()
.width(50)
.height(50)
Text('加载中...')
.fontSize(16)
.margin({ top: 16 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
.width('100%')
.height('100%')
}
// 构建器...
}
6.2 列表性能优化
对于包含大量菜单项的侧边栏,我们可以使用LazyForEach
优化列表性能:
// 菜单项数据源
class MenuItemDataSource implements IDataSource {
private menuItems: MenuItem[]
private listener: DataChangeListener
constructor(menuItems: MenuItem[]) {
this.menuItems = menuItems
}
totalCount(): number {
return this.menuItems.length
}
getData(index: number): MenuItem {
return this.menuItems[index]
}
registerDataChangeListener(listener: DataChangeListener): void {
this.listener = listener
}
unregisterDataChangeListener() {
}
}
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 菜单项数据源
private menuItemDataSource: MenuItemDataSource = new MenuItemDataSource([])
aboutToAppear() {
// 加载菜单项
this.loadMenuItems()
// 初始化数据源
this.menuItemDataSource = new MenuItemDataSource(this.menuItems)
// 其他初始化...
}
@Builder SideBarContent() {
Column() {
// 用户信息区域...
// 菜单选项
List() {
LazyForEach(this.menuItemDataSource, (item: MenuItem) => {
ListItem() {
this.renderMenuItem(item)
}
}, (item: MenuItem) => item.id.toString())
}
.width('100%')
.margin({ top: 50 })
// 底部退出按钮...
}
.width('100%')
.height('100%')
.backgroundColor(this.isDarkMode ? '#121212' : '#FFFFFF')
}
// 构建器...
}
6.3 缓存优化
为了减少不必要的重新渲染,我们可以使用缓存优化:
@Entry
@Component
struct MobileMenu {
// 状态变量...
// 缓存的主内容
@State cachedMainContent: Record<number, Object> = {}
// 构建主内容
@Builder MainContent() {
Column() {
// 顶部应用栏...
// 内容区域
Column() {
// 使用缓存的内容或创建新内容
if (this.cachedMainContent[this.currentIndex]) {
// 使用缓存的内容
this.cachedMainContent[this.currentIndex]
} else {
// 创建新内容并缓存
if (this.currentIndex === 0) {
this.HomeContent()
} else if (this.currentIndex === 1) {
this.MessageContent()
} else if (this.currentIndex === 2) {
this.FavoriteContent()
} else if (this.currentIndex === 3) {
this.SettingsContent()
} else if (this.currentIndex === 4) {
this.AboutContent()
}
}
}
.width('100%')
.layoutWeight(1)
.backgroundColor('#F5F5F5')
}
.width('100%')
.height('100%')
}
// 缓存内容
private cacheContent(index: number, content: Object) {
this.cachedMainContent[index] = content
}
// 清除缓存
private clearCache() {
this.cachedMainContent = {}
}
// 构建器...
}
七、总结
在本教程中,我们深入探讨了如何通过状态管理和交互功能增强,使HarmonyOS NEXT的SideBarContainer
组件的Overlay
模式侧边栏更加智能和易用。 通过这些增强功能,我们的移动端抽屉菜单不仅具有基本的导航功能,还提供了更好的用户体验和更丰富的交互方式。这些技术可以应用于各种移动应用场景,例如社交应用、电商应用、内容应用等。
希望本教程能够帮助你更好地理解和使用HarmonyOS NEXT的SideBarContainer
组件的Overlay
模式,创建出更加优秀的移动应用。
