关键词:折叠屏、navigation、router、路由、分栏、UI
随着科技的发展,手机设备形态也由一面屏向多面屏进行发展,那么软件的UI适配也面临着问题,本篇文章主要解决大屏设备的页面 UI 适配问题,如折叠屏,平板,若不想单独对页面 UI 进行大屏设备的响应式布局的开发,那么可使用官方提供的 navigation 跳转方式,对页面进行分栏展示,从而确保应用能给用户提供良好的 UI 交互,避免出现 “大号APP” 的问题。
本期文章以双折叠屏设备为例,使用 navigation 对页面进行动态分栏跳转及自定义 router 的封装与开发改造。需要注意的是,使用 Navigation 对折叠屏设备进行分栏模式适配后,基础单屏设备(标准屏)并不会出现分栏效果,跟 router 跳转无差异,所以可放心使用。
本期完整demo已提交至Gitee:columnDivision: 【HarmonyOS NEXT】使用 Navigation 对折叠屏设备页面进行分栏展示,优化 UI 交互
目录
效果演示
2. 使用 Localstorage 存储路由栈
4. 创建 route_map.json 文件,并配置该文件
5. 自定义 router 封装
6. 使用封装的 router 跳转页面
效果演示
未使用 navigation
使用 navigation(展开与折叠状态)
1. 使用 Navigation() 组件包裹首页面,绑定路由栈
应用启动则会进入第一个页面,展示的第一个页面需要使用 Navigation() 对根容器进行包裹,并对其进行路由栈的绑定,后续跳转则需使用绑定的路由栈 pageInfos 进行跳转。
2. 使用 Localstorage 存储路由栈
在第一步中创建的 pageInfos 需要被应用共享去使用,我使用的是 Localstorage 方式去存取,也可使用 AppStorage 或单例等其他形式,根据自身业务与开发习惯跳转即可。
因为我使用的是 Localstorage 页面级状态存储的方式,若想被整个 stage 共享,那么需要在 ability 加载时就创建好 Localstorage 并在 loadContent 时传入第二个参数。
后续使用即可利用 getShared 获取当前 stage 共享的 localstorage,如:
// 设置 localstorage
LocalStorage.getShared().setOrCreate("NavPathStack", pageInfos)// 获取 localstorage
LocalStorage.getShared().get("NavPathStack") as NavPathStack
3. 使用 NavDestination() 组件包裹目标页面,创建页面构建函数
使用 navigation 跳转页面,则需要对跳转的目标页面根容器进行改造,外层包裹 NavDestination 组件,并设置 onBackPressed() 回调,因为我们要实现动态的分栏效果,所以在返回手势触发时也需要进行路由栈的计算,详细可查看第 5 步。
组件改造后需要创建构建函数,函数中引入当前的页面组件并进行导出,此处也不可缺少,因为需要在配置文件中进行设置。
4. 创建 route_map.json 文件,并配置该文件
在鸿蒙应用开发过程中,页面跳转需要使用到 router ,并在 main_pages.json 中配置页面路由。同理,若想使用 navigation 进行跳转,也需要进行相关的路由配置,不过我们需要手动在 resources/base/profile 下创建 route_map.json 文件,在创建的文件中进行如下路由配置。
字段解释:
name:路由名称,后续跳转时需要使用该名称进行跳转
pageSourceFile:对应页面代码文件的路径
buildFunction:在前一步中改造后导出的构建函数 Builder 名称
创建完 route_map.json 并不能立即进行跳转,还需要在 module.json5 中生效该文件,在 module 对象下设置 routerMap 字段,值为 $profile:route_map ,代表刚才在 profile 中创建的 route_map 文件。
5. 自定义 router 封装
Navigation 存在默认分栏比例,折叠屏设备在 app 一启动即为分栏展示(右半屏白屏展示,左半屏展示首页),那我们如果想做到 app 一启动,首先全屏展示首页,当需要跳转到下一页时再进行分栏,那么我们可以通过封装自定义 router ,在页面跳转及页面关闭时计算当前路由栈的个数,并动态设置 navBarWidth 分栏的宽度即可实现该效果。
如下代码 router 封装较为简易,仅做演示,我使用的是根据 name 进行跳转,需要根据自身业务进行调整,如页面跳转传参等。
该 Router 类使用 @Observed 装饰,是为了使用类中 navBarWidth 动态属性,在首页面使用 @State 装饰接收 MyRouter,并在首页面的 Navigation 的 .navBarWidth() 中设置 this.MyRouter.navBarWidth ,当类中 navBarWidth 发生变化时,即可进行分栏比例 UI 的动态更新。
/*** 简易 router 封装*/
@Observed
export class Router {navBarWidth: string = "100%"/*** 跳转* @param name* @param pageInfos*/pushDestinationByName(name: string, pageInfos: NavPathStack) {pageInfos.pushDestinationByName(name, null)this.calcNavBarWidth(pageInfos)}backPress(pageInfos: NavPathStack) {let popR: NavPathInfo | undefined = pageInfos.pop()this.calcNavBarWidth(pageInfos)return popR}calcNavBarWidth(pageInfos: NavPathStack) {if (pageInfos.getAllPathName().length == 0) {this.navBarWidth = "100%"} else {this.navBarWidth = "50%"}console.log("luvi pageInfos > " + JSON.stringify(pageInfos))}
}export const MyRouter = new Router()
6. 使用封装的 router 跳转页面
在第 2 步中,创建 Builder 构建函数后并配置了 route_map,并设置了页面名称 name,在第 5 步中封装的 pushDestinationByName 方法需要传递目标页面的 name 与路由栈,方法内则会使用该路由栈进行页面跳转,并计算分栏宽度。
// 直接使用封装的 MyRouter 进行跳转即可
MyRouter.pushDestinationByName("NewsPage", this.pageInfos)