本文旨在深入探讨华为鸿蒙HarmonyOS Next系统的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。

在HarmonyOS Next应用开发中,多栏导航与内容展示布局是常见的界面设计模式,它能有效提升用户操作效率和信息展示效果。接下来,咱们深入剖析单栏/双栏/三栏布局的实现逻辑,以及如何在大屏设备上优化三分栏导航体验。

单栏/双栏/三栏布局的核心逻辑(SideBarContainer + Navigation组合使用)

在HarmonyOS Next中,实现单栏/双栏/三栏布局的关键在于SideBarContainerNavigation组件的巧妙组合。SideBarContainer用于创建侧边栏,可放置导航菜单等功能模块;Navigation组件则负责管理页面内容的显示和切换逻辑。

以一个文件管理应用为例,单栏布局通常适用于小屏幕设备,此时侧边栏可能隐藏,用户通过点击操作唤起侧边栏进行功能切换。双栏布局在中等屏幕尺寸设备上较为常见,侧边栏和内容区同时显示,方便用户快速导航和查看内容。三栏布局则更适合大屏设备,将界面分为侧边导航区、列表导航区和内容区,进一步提升操作效率。

下面是一个简单的代码示例,展示基本的组合使用方式:

// 工程配置文件module.json5中配置{"routerMap": "$profile:route_map"}
// route_map.json
{
    "routerMap": [
        {
            "name": "FileListPage",
            "pageSourceFile": "src/main/ets/pages/FileListPage.ets",
            "buildFunction": "FileListPageBuilder",
            "data": {
                "description": "文件列表页面"
            }
        },
        {
            "name": "FileDetailPage",
            "pageSourceFile": "src/main/ets/pages/FileDetailPage.ets",
            "buildFunction": "FileDetailPageBuilder"
        }
    ]
}

// FileListPage.ets
@Builder
export function FileListPageBuilder() {
    return FileListPage();
}

@Component
export struct FileListPage {
    build() {
        Column() {
            NavDestination() {
                Text('文件列表页面内容').fontSize(20).padding(20)
            }
              .title('文件列表')
        }
    }
}

// FileDetailPage.ets
@Builder
export function FileDetailPageBuilder() {
    return FileDetailPage();
}

@Component
export struct FileDetailPage {
    build() {
        Column() {
            NavDestination() {
                Text('文件详情页面内容').fontSize(20).padding(20)
            }
              .title('文件详情')
        }
    }
}

@Entry
@Component
struct MultiColumnLayout {
    @State showSideBar: boolean = false;
    pageInfos: NavPathStack = new NavPathStack();
    @State navItems: { label: string, pagePath: string }[] = [
        {
            label: '文件列表',
            pagePath: 'FileListPage'
        },
        {
            label: '文件详情',
            pagePath: 'FileDetailPage'
        }
    ];
    build() {
        SideBarContainer(SideBarContainerType.Overlay) {
            Column() {
                List() {
                    ForEach(this.navItems, (item) => {
                        ListItem() {
                            Text(item.label).fontSize(20).onClick(() => {
                                this.pageInfos.clear();
                                this.pageInfos.pushPath({ name: item.pagePath });
                            })
                        }
                    })
                }
            }
              .width('100%')
              .height('100%')
              .justifyContent(FlexAlign.SpaceEvenly)
              .backgroundColor('#F1F3F5')
            Column() {
                Navigation(this.pageInfos) {
                    List() {
                        ForEach(this.navItems, (item) => {
                            ListItem() {
                                Text(item.label).fontSize(20).onClick(() => {
                                    this.pageInfos.pushPath({ name: item.pagePath });
                                })
                            }
                        })
                    }
                }
                  .width('100%')
                  .height('100%')
                  .hideToolBar(true)
            }
        }
          .sideBarWidth(240)
          .showSideBar(this.showSideBar)
          .onChange((isOpen: boolean) => {
                this.showSideBar = isOpen;
            })
    }
}

在上述代码中,SideBarContainer包含了侧边栏的导航菜单,Navigation组件负责管理页面内容的显示和切换。通过点击侧边栏的菜单选项,pageInfos的路径更新,从而实现不同页面内容的展示。

动态调整布局模式(NavigationMode.Auto如何工作?)

Navigation组件的NavigationMode.Auto模式是实现布局动态调整的关键。该模式下,Navigation组件会根据应用窗口尺寸自动选择合适的模式:当窗口宽度小于520vp时,采用Stack模式显示,即页面内容堆叠显示;当窗口宽度大于等于520vp时,采用Split模式显示,将页面分为两栏或更多栏展示。

当窗口尺寸发生改变时,Navigation组件会自动在Stack模式和Split模式之间切换。例如,在一个新闻阅读应用中,小屏幕手机上可能以Stack模式显示文章列表和详情,用户点击列表项后,文章详情覆盖在列表上方;而在平板设备上,窗口宽度较大,Navigation组件会自动切换到Split模式,文章列表和详情可以同时显示在不同栏中,方便用户对比和操作。

在实际使用中,无需开发者手动编写复杂的切换逻辑,只需要将Navigation组件的mode属性设置为NavigationMode.Auto,并合理配置相关子组件,就能轻松实现布局的动态调整,为用户提供更加流畅的操作体验。

如何在大屏设备上优化三分栏导航体验(示例代码)

在大屏设备上,三分栏导航可以充分利用屏幕空间,提升用户操作效率。为了优化三分栏导航体验,我们可以从多个方面入手,如调整各栏宽度、优化交互效果等。

以下是一个优化后的三分栏布局示例代码:

// 工程配置文件module.json5中配置{"routerMap": "$profile:route_map"}
// route_map.json
{
    "routerMap": [
        {
            "name": "MainPage",
            "pageSourceFile": "src/main/ets/pages/MainPage.ets",
            "buildFunction": "MainPageBuilder",
            "data": {
                "description": "主页面"
            }
        },
        {
            "name": "SubPage1",
            "pageSourceFile": "src/main/ets/pages/SubPage1.ets",
            "buildFunction": "SubPage1Builder"
        },
        {
            "name": "SubPage2",
            "pageSourceFile": "src/main/ets/pages/SubPage2.ets",
            "buildFunction": "SubPage2Builder"
        }
    ]
}

// MainPage.ets
@Builder
export function MainPageBuilder() {
    return MainPage();
}

@Component
export struct MainPage {
    build() {
        Column() {
            NavDestination() {
                Text('这是主页面内容').fontSize(20).padding(20)
            }
              .title('主页面')
        }
    }
}

// SubPage1.ets
@Builder
export function SubPage1Builder() {
    return SubPage1();
}

@Component
export struct SubPage1 {
    build() {
        Column() {
            NavDestination() {
                Text('这是子页面1的内容').fontSize(20).padding(20)
            }
              .title('子页面1')
        }
    }
}

// SubPage2.ets
@Builder
export function SubPage2Builder() {
    return SubPage2();
}

@Component
export struct SubPage2 {
    build() {
        Column() {
            NavDestination() {
                Text('这是子页面2的内容').fontSize(20).padding(20)
            }
              .title('子页面2')
        }
    }
}

@Entry
@Component
struct OptimizedTripleColumnLayout {
    @State curBp: string = 'lg';
    @State showSideBar: boolean = true;
    pageInfos: NavPathStack = new NavPathStack();
    @State navItems: { label: string, pagePath: string }[] = [
        {
            label: '主页面',
            pagePath: 'MainPage'
        },
        {
            label: '子页面1',
            pagePath: 'SubPage1'
        },
        {
            label: '子页面2',
            pagePath: 'SubPage2'
        }
    ];
    @Builder NavigationTitle() {
        Column() {
            Text('应用名称').fontColor('#000000').fontSize(24).width('100%').height('100%').align(Alignment.BottomStart).margin({ left: '5%' })
        }
          .alignItems(HorizontalAlign.Start)
    }
    build() {
        SideBarContainer() {
            Column() {
                List() {
                    ForEach(this.navItems, (item) => {
                        ListItem() {
                            Text(item.label).fontSize(20).onClick(() => {
                                this.pageInfos.clear();
                                this.pageInfos.pushPath({ name: item.pagePath });
                            })
                        }
                    })
                }
                  .divider({ strokeWidth: 5, color: '#F1F3F5' })
            }
              .width('100%')
              .height('100%')
              .justifyContent(FlexAlign.SpaceEvenly)
              .backgroundColor('#F1F3F5')
            Column() {
                Navigation(this.pageInfos) {
                    List() {
                        ForEach(this.navItems, (item) => {
                            ListItem() {
                                Text(item.label).fontSize(20).onClick(() => {
                                    this.pageInfos.pushPath({ name: item.pagePath });
                                })
                            }
                        })
                    }
                }
                  .mode(NavigationMode.Auto)
                  .minContentWidth(600)
                  .navBarWidth(240)
                  .backgroundColor('#FFFFFF')
                  .height('100%')
                  .width('100%')
                  .hideToolBar(true)
                  .title(this.NavigationTitle)
            }
        }
          .sideBarWidth(240)
          .minContentWidth(600)
          .showSideBar(this.showSideBar)
          .onChange((isOpen: boolean) => {
                this.showSideBar = isOpen;
            })
          .onBreakpointChange((breakpoint: string) => {
                this.curBp = breakpoint;
                if (breakpoint ==='sm') {
                    this.showSideBar = false;
                } else {
                    this.showSideBar = true;
                }
            })
    }
}

在这个示例中,通过SideBarContainerNavigation组件构建了三分栏布局。SideBarContainersideBarWidth属性设置了侧边栏宽度为240,minContentWidth属性设置了内容区的最小宽度为600,确保在不同窗口尺寸下各栏布局合理。Navigation组件的mode属性设置为NavigationMode.Auto,实现了根据窗口尺寸自动切换布局模式。同时,通过onBreakpointChange事件监听断点变化,在小屏幕(如sm断点)下隐藏侧边栏,提升小屏幕设备的显示效果;在大屏设备上,保持侧边栏显示,方便用户操作。通过这些优化措施,能够为大屏设备用户提供更加高效、便捷的导航体验。


SameX
1 声望2 粉丝