【HarmonyOS】HMRouter配置与基本使用
文章目录
- 一、HMRouter简介
- 1、概念
- 2、特性
- 二、依赖配置
- 1、安装依赖
- 2、编译插件配置
- 3、工程配置
- 三、初步探索
- 1、初始化路由框架
- 2、定义路由接口
- 3、定义拦截器
- 4、定义生命周期
- 5、路由跳转使用
- 四、标签分类
- 1、 路由标签@HMRouter
- 2、拦截器标签 @HMInterceptor
- 3、生命周期标签 @HMLifecycle
- 五、HMRouter接口和属性列表
- 1、HMNavigation, 路由容器组件
- 1.1 HMNavigationOption
- 2、HMRouterMgr类
- 2.1 HMRouterConfig类
- 2.2 HMRouterPathInfo类
- 2.3 HMRouterPathCallback类
- 2.4 HMPopInfo类
- 2.5 HMParamType枚举
- 2.6 HMPageParam类
- 2.7 HMPageInstance类
- 2.8 HMPageLifecycle类
- 六、部分案例介绍
- 1、页面跳转与返回
- 2、多次页面跳转,返回指定页面
- 3、应用未登录,点击跳转登录页的校验场景
- 4、实现单例页面的跳转
- 5、实现弹窗类型的页面
- 6、返回时弹窗,提示用户是否确认返回
- 7、首页两次返回退出应用
一、HMRouter简介
开发者文档:HMRouter参考文档
1、概念
HMRouter的出现是为了解决页面跳转的问题,它对系统Navigation进行封装,集成了Navigation、NavDestination等的系统能力,提供了可复用的路由拦截、自定义转场动画,并且在跳转传参、额外的生命周期、服务型路由方面对系统能力进行了扩展。
2、特性
- 使用自定义注解实现路由跳转。
- 支持HAR,HSP,HAP。
- 支持路由拦截、路由生命周期。
- 简化自定义动画配置:配置全局动画,单独指定某个页面的切换动画。
- 支持不同的页面类型。
二、依赖配置
1、安装依赖
在终端中使用ohpm
安装依赖。
ohpm install @hadss/hmrouter
如图,即为添加成功。同时上侧会出现蓝条,点击Sync now
(立即同步)即可。
或者按需在模块中配置运行时依赖,修改
oh-package.json5
{"dependencies": {"@hadss/hmrouter": "latest"}
}
2、编译插件配置
- 修改工程的
hvigor/hvigor-config.json
文件。
{"dependencies": {"@hadss/hmrouter-plugin": "latest"},// ...其他配置
}
同上点击Sync now
即可。
- 在使用到HMRouter的模块中引入路由编译插件,修改
hvigorfile.ts
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
import { hapPlugin } from '@hadss/hmrouter-plugin';export default {system: hapTasks,plugins: [hapPlugin()]
}
// 使用HMRouter标签的模块均需要配置,与模块类型保持一致
// 如果模块是Har则使用harPlugin(), 模块是Hsp则使用hspPlugin(), 模块是Hap则使用hapPlugin()
- 项目根目录或者模块目录创建路由编译插件配置文件
hmrouter_config.json
(推荐)
{// 如果不配置则扫描src/main/ets目录,对代码进行全量扫描,如果配置则数组不能为空,建议配置指定目录可缩短编译耗时"scanDir": ["src/main/ets/components","src/main/ets/interceptors"],// 默认为false,调试排除错误时可以改成true,不删除编译产物"saveGeneratedFile": false,// 默认为false,不自动配置混淆规则,只会生成hmrouter_obfuscation_rules.txt文件帮助开发者配置混淆文件;如果设置为true,会自动配置混淆规则,并删除hmrouter_obfuscation_rules.txt文件"autoObfuscation": false,// 默认模板文件,不配置时使用插件内置模板"defaultPageTemplate": "./templates/defaultTemplate.ejs",// 特殊页面模版文件,匹配原则支持文件通配符"customPageTemplate": [{"srcPath": ["**/component/Home/**/*.ets"],"templatePath": "./templates/home_shopping_template.ejs"},{"srcPath": ["**/common/**/*.ets"],"templatePath": "./templates/common_template.ejs"},{"srcPath": ["**/live/**/*.ets"],"templatePath": "./templates/live_template.ejs"}]
}
customPageTemplate字段中srcPath使用标准文件通配符规则,相关符号描述如下* (星号):匹配任意数量的字符(包括零个字符),但不匹配路径分隔符(如 /)。例如,*.txt 可以匹配所有 .txt 结尾的文件。
** (双星号):匹配任意深度的目录或文件。可以用于递归搜索子目录。例如,**/*.js 可以匹配当前目录及其所有子目录中的所有 .js 文件。
/ (正斜杠):路径分隔符,用于分隔目录和文件名称。
! (感叹号):用于排除某些文件或目录,通常结合其他通配符使用。例如,!node_modules/** 可以排除 node_modules 目录及其子目录中的所有文件。
? (问号):匹配任意一个字符(不包括路径分隔符)。例如,file?.txt 可以匹配 file1.txt 或 fileA.txt,但不匹配 file123.txt。
[abc] (方括号):匹配方括号内的任意一个字符。例如,file[123].txt 可以匹配 file1.txt、file2.txt 或 file3.txt。
[!abc] (方括号加感叹号):匹配不在方括号内的任意一个字符。例如,file[!123].txt 可以匹配 fileA.txt,但不匹配 file1.txt、file2.txt 或 file3.txt。
{a,b,c} (大括号):匹配大括号中的任意一个模式。例如,*.{jpg,png,gif} 可以匹配 .jpg、.png 或 .gif 格式的文件。
\ (反斜杠):转义字符,用于匹配特殊字符本身。例如,如果要匹配文件名中带有 * 的文件,可以使用 * 进行转义。
hmrouter_config.json 配置
hmrouter_config.json
文件用于配置该插件在编译期的行为。
配置文件读取规则为 模块 > 工程 > 默认
优先使用本模块内的配置,如果没有配置,则找模块目录的上级目录(最多找三层目录,找到则停止),若找不到则使用默认配置
1.0.0-rc.6 版本开始,支持混淆配置
autoObfuscation
1.0.0-rc.9 版本开始,支持自定义模版配置
customPageTemplate
配置项 | 类型 | 是否必填 | 说明 |
---|---|---|---|
scanDir | array | 否 | 指定扫描当前模块路径,默认值为src/main/ets |
saveGeneratedFile | boolean | 否 | 默认为 false,不保留插件自动生成的代码,如果需要保留,需要设置为 true |
autoObfuscation | boolean | 否 | 默认为 false,不自动配置混淆规则,只会生成hmrouter_obfuscation_rules.txt 文件帮助开发者配置混淆规则;如果设置为 true,会自动配置混淆规则,并在编译完成后删除hmrouter_obfuscation_rules.txt 文件 |
defaultPageTemplate | string | 否 | 默认模版路径,相对于hmrouter_config.json文件,例如:./templates/defaultTemplate.ejs |
customPageTemplate | object | 否 | srcPath为匹配的代码文件路径,支持通配符,templatePath为模版路径,可以实现不同的代码使用不同的模版来生成 |
- 在
HMRouterPlugin
中,EJS模板用于生成动态页面或组件。
模板文件中至少需要包含
NavDestination
组件代码和相对应的build
函数,缺少会导致编译失败或者页面白屏,插件中会内置一套默认模板,其中包含了页面展示、生命周期注册、转场动画注册
默认模板介绍
插件默认会根据如下模板来生成NavDestination
页面代码,如有自定义模板的需求,建议先阅读内置模板的介绍在做更改,书写自定义模板时建议在内置模板基础上添加代码,删除内置模板相关代码可能会导致编译失败、生命周期生效、转场动画失效等问题。
默认模版viewBuilder.ejs
:
import { <%= componentName %> } from '<%= importPath %>'
import { TemplateService, TranslateOption, ScaleOption, OpacityOption } from '@hadss/hmrouter'@Builder
export function <%= componentName %>Builder(name: string, param: Object) {<%= componentName %>Generated()
}@Component
export struct <%= componentName %>Generated {@State translateOption: TranslateOption = new TranslateOption()@State scaleOption: ScaleOption = new ScaleOption()@State opacityOption: OpacityOption = new OpacityOption()private pageUrl: string = '<%= pageUrl %>'private ndId: string = ''private navigationId: string = ''aboutToAppear(): void {this.navigationId = this.queryNavigationInfo()!.navigationId;TemplateService.aboutToAppear(this.navigationId, this.pageUrl, <%= dialog %>,this.translateOption, this.scaleOption, this.opacityOption)}aboutToDisappear(): void {TemplateService.aboutToDisappear(this.navigationId, this.pageUrl, this.ndId)}build() {NavDestination() {<%= componentName %>()}<% if(dialog){ %>.mode(NavDestinationMode.DIALOG)<% } %>.hideTitleBar(true).gesture(PanGesture().onActionStart((event: GestureEvent) => {TemplateService.interactiveStart(this.navigationId, this.ndId, event)}).onActionUpdate((event: GestureEvent) =>{TemplateService.interactiveProgress(this.navigationId, this.ndId, event)}).onActionEnd((event: GestureEvent) =>{TemplateService.interactiveFinish(this.navigationId, this.ndId, event)})).translate(this.translateOption).scale(this.scaleOption).opacity(this.opacityOption.opacity).onAppear(() => {TemplateService.onAppear(this.navigationId, this.pageUrl, this.ndId)}).onDisAppear(() => {TemplateService.onDisAppear(this.navigationId, this.pageUrl, this.ndId)}).onShown(() => {TemplateService.onShown(this.navigationId, this.pageUrl, this.ndId)}).onHidden(() => {TemplateService.onHidden(this.navigationId, this.pageUrl, this.ndId)}).onWillAppear(() => {TemplateService.onWillAppear(this.navigationId, this.pageUrl)}).onWillDisappear(() => {TemplateService.onWillDisappear(this.navigationId, this.pageUrl, this.ndId)}).onWillShow(() => {TemplateService.onWillShow(this.navigationId, this.pageUrl, this.ndId)}).onWillHide(() => {TemplateService.onWillHide(this.navigationId, this.pageUrl, this.ndId)}).onReady((navContext: NavDestinationContext) => {this.ndId = navContext.navDestinationId!TemplateService.onReady(this.navigationId, this.pageUrl, navContext)}).onBackPressed(() => {return TemplateService.onBackPressed(this.navigationId, this.pageUrl, this.ndId)})}
}
模板变量
属性 | 描述 |
---|---|
pageUrl | 标签中配置的pageUrl的值 |
importPath | 原组件的导入路径 |
componentName | 原组件名 |
dialog | 是否dialog页面 |
generatorViewName | 生成的文件名 |
TemplateService内置模版方法
该类中封装了一系列在模板中需要用到的注册、初始化、事件回调接口
接口 | 参数 | 返回值 | 接口描述 |
---|---|---|---|
static aboutToAppear | navigationId: string,pageUrl: string, dialog: boolean, translateOption: TranslateOption, scaleOption: ScaleOption, opacityOption: OpacityOption | void | 注册接口,用于模板代码中注册动画与生命周期 |
static aboutToDisappear | navigationId: string, pageUrl: string, ndId: string | void | 销毁,用户销毁一个页面的动画与生命周期实例 |
static onDisAppear | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
static onAppear | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
static onShown | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
static onHidden | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
static onWillAppear | navigationId: string, pageUrl: string | void | NavDestination生命周期 |
static onWillDisappear | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
static onWillShow | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
static onWillHide | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
static onReady | navigationId: string, pageUrl: string, navContext: NavDestinationContext | void | NavDestination生命周期 |
static onBackPressed | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
static interactiveStart | navigationId: string, ndId: string, event: GestureEvent | void | 手势转场动画触发 |
static interactiveFinish | navigationId: string, ndId: string, event: GestureEvent | void | 手势转场动画更新 |
static interactiveProgress | navigationId: string, ndId: string, event: GestureEvent | void | 手势转场动画结束 |
3、工程配置
由于拦截器、生命周期和自定义转场动画会在运行时动态创建实例,因此需要进行如下配置,使得HMRouter路由框架可以动态导入项目中的模块
- 在工程目录下的
build-profile.json5
中,配置useNormalizedOHMUrl
属性为true
原本是true
就不用管了。
- 在
oh-package.json5
中配置对Har和Hsp的依赖,这里需要注意依赖的模块名称需要与module.json5
中moduleName
、oh-package.json5
中name
保持一致。
如果只是一个
entry
目录就不用管这个点。
三、初步探索
1、初始化路由框架
在EntryAbility
中onCreate
初始化。
import { HMRouterMgr } from '@hadss/hmrouter';
HMRouterMgr.init({context: this.context})
另一种方法可以参考:在启动框架中初始化
2、定义路由接口
HMRouter依赖系统Navigation能力,所以必须在页面中定义一个HMNavigation
容器,并设置相关参数。
import { HMDefaultGlobalAnimator, HMNavigation, HMRouterMgr } from '@hadss/hmrouter';
import { AttributeUpdater, uiObserver } from '@kit.ArkUI';class MyNavModifier extends AttributeUpdater<NavigationAttribute> {initializeModifier(instance: NavigationAttribute): void {instance.hideNavBar(true);}
}@Entry
@Component
struct Index {modifier: MyNavModifier = new MyNavModifier();build() {// @Entry中需要再套一层容器组件,Column或者StackColumn() {// [Start hum_navigation] 使用HMNavigation容器HMNavigation({navigationId: 'mainNavigationId',homePageUrl: 'MainPage',options: {standardAnimator: HMDefaultGlobalAnimator.STANDARD_ANIMATOR,modifier: this.modifier}});// [End hum_navigation]}.height('100%').width('100%').expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]);}
}
Navigation的系统属性通过
modifier
传递,部分modifier
不支持的属性使用options
设置
3、定义拦截器
@HMInterceptor({ interceptorName: 'JumpInfoInterceptor', global: true })
export class JumpInfoInterceptor implements IHMInterceptor {handle(info: HMInterceptorInfo): HMInterceptorAction {let connectionInfo: string = info.type === 'push' ? 'jump to' : 'back to';Logger.info(`${info.srcName} ${connectionInfo} ${info.targetName}`)return HMInterceptorAction.DO_NEXT;}
}
4、定义生命周期
@HMLifecycle({ lifecycleName: 'PageDurationLifecycle' })
export class PageDurationLifecycle implements IHMLifecycle {private time: number = 0;onShown(ctx: HMLifecycleContext): void {this.time = new Date().getTime();}onHidden(ctx: HMLifecycleContext): void {const duration = new Date().getTime() - this.time;Logger.info(`Page ${ctx.navContext?.pathInfo.name} stay ${duration}`);}
}
5、路由跳转使用
使用@HMRouter
标签定义页面,绑定拦截器、生命周期及自定义转场动画
@HMRouter({ pageUrl: '/pageB', lifecycle: 'pageLifecycle', animator: 'pageAnimator' })
@Component
export struct PageB {// 获取生命周期中定义的状态变量@State model: ObservedModel | null = (HMRouterMgr.getCurrentLifecycleOwner().getLifecycle() as PageLifecycle).model@State param: HMPageParam = HMRouterMgr.getCurrentParam(HMParamType.all)build() {Column() {Text(`${this.model?.property}`)Text(`${this.param.paramsMap?.get('msg')}`)}}
}
定义页面PageA,使用HMRouterMgr.push
执行路由跳转至PageB
const PAGE_URL: string = '/pageA'@HMRouter({ pageUrl: PAGE_URL })
@Component
export struct PageA {build() {Column() {Button('Push').onClick(() => {HMRouterMgr.push({ pageUrl: 'pageB?msg=abcdef' })})}
}
}
路由跳转支持URL带参数的方式,例如定义的页面pageUrl:
/pages1/users
,跳转时可以指定pageUrl为:/pages1/users?msg=1234
通过 URL 参数的形式传递了数据
msg=abcdef
。这里的
?
是 URL 中分隔路径和参数的标识符,?
后面的部分msg=abcdef
是传递的参数:通过HMRouterMgr.getCurrentParam传入HMParamType.all获取URL的参数内容
// 假设在 pageB 中获取参数 const params = HMRouterMgr.getCurrentParams(HMParamType.all); console.log(param.paramsMap?.get('msg')); // 输出: 1234
四、标签分类
1、 路由标签@HMRouter
@HMRouter(...)
标签使用在自定义组件struct
上,且该自定义组件需要添加export
关键字。
- pageUrl: string, 用来表示NavDestination,必填。
- isRegex:boolean, 标识配置的pageUrl是否是正则表达式,非必填。
- regexPriority: number, pageUrl正则匹配优先级,数字越大越先匹配,默认值为0,优先级相同时,不保证先后顺序,非必填,默认为0。
- dialog: boolean, 是否是Dialog类型页面,非必填,默认为false。
- singleton: boolean, 是否是单例页面,单例页面即表示在一个HMNavigation容器中,只有一个此页面,非必填,默认为false。
- interceptors: string[],
@HMInterceptor
标记的拦截器名称列表,非必填。 - lifecycle: string,
@HMLifecycle
标记的生命周期处理实例,非必填。 - animator: string,
@HMAnimator
标记的自定义转场实例,非必填。
2、拦截器标签 @HMInterceptor
标记在实现了IHMInterceptor
的对象上,声明此对象为一个拦截器。
- interceptorName: string, 拦截器名称,必填。
- priority: number, 拦截器优先级,数字越大优先级越高,非必填,默认为9;
- global: boolean, 是否为全局拦截器,当配置为true时,所有跳转均过此拦截器;默认为false,当为false时需要配置在@HMRouter的interceptors中才生效。
执行时机:
在路由栈发生变化前,转场动画发生前进行回调。 1.当发生push/replace路由时,pageUrl为空时,拦截器不会执行,需传入pageUrl路径;
2.当跳转pageUrl目标页面不存在时,执行全局以及发起页面拦截器,当拦截器未执行DO_REJECT时,然后执行路由的onLost回调
3.当跳转pageUrl目标页面存在时,执行全局,发起页面和目标页面的拦截器;
3、生命周期标签 @HMLifecycle
@HMLifecycle(lifecycleName, priority, global)
标记在实现了IHMLifecycle的对象上,声明此对象为一个自定义生命周期处理器。
- lifecycleName: string, 自定义生命周期处理器名称,必填。
- priority: number, 生命周期优先级,数字越大优先级越高,非必填,默认为9;
- global: boolean, 是否为全局生命周期,当配置为true时,所有页面生命周期事件会转发到此对象;默认为false。
生命周期触发顺序:
按照优先级顺序触发,不区分自定义或者全局生命周期,优先级相同时先执行@HMRouter中定义的自定义生命周期。
五、HMRouter接口和属性列表
开发文档
1、HMNavigation, 路由容器组件
参数 | 说明 |
---|---|
navigationId | 需要开发者指定,并确保全局唯一,否则运行时输出报错日志 |
homePageUrl | 需要与@HMRouter(pageUrl)中的pageUrl一致,表示HMNavigation默认加载的页面。 |
navigationOption | 指定该HMNavigation的全局参数选项NavigationOption,保留可扩展性。 |
1.1 HMNavigationOption
参数 | 说明 |
---|---|
modifier:AttributeUpdater | Navigation动态属性设置 |
standardAnimator:IHMAnimator.Effect | 页面全局动画配置 |
dialogAnimator:IHMAnimator.Effect | 弹窗全局动画配置 |
title : NavTitle | 系统Title设置 |
menus: Array | CustomBuilder | 系统Menu设置 |
toolbar: Array | CustomBuilder | 系统Toolbar设置 |
systemBarStyle: Optional | 系统SystemBar设置 |
2、HMRouterMgr类
核心类,提供初始化方法,日志使能方法,路由能力
接口 | 参数 | 返回值 | 接口描述 |
---|---|---|---|
static init | config: HMRouterConfig | void | 初始化HMRouter,采用多线程初始化,将标签里面的页面与拦截器、转场动画、生命周期的映射加载到内存 |
static openLog | level : ‘DEBUG’|‘INFO’ | void | 使能HMRouter日志。level为info时,表示打开Info日志,level为debug时,表示打开info和debug日志。论述是否调用openLog,warnning和error日志均正常打印。 |
static push | pathInfo: HMRouterPathInfo, callback? HMRouterPathCallback | void | 实现路由跳转,提供跳转回调 |
static replace | pathInfo: HMRouterPathInfo, callback?: HMRouterPathCallback | void | 实现路由跳转,提供跳转回调 |
static pop | pathInfo?: HMRouterPathInfo, skipedLayerNumber?: number | void | 实现路由返回, skipedLayerNumber返回是跳跃的页面层数,默认为0(表示返回上级页面,1表示跳过一级页面返回,即同时两个页面出栈),以HMRouterPathInfo.pageUrl为首选,skipedLayerNumber为次选 |
2.1 HMRouterConfig类
初始化配置
参数 | 说明 |
---|---|
context:UIAbilityContext | UIAbility上下文,用于初始化时读取路由配置信息 |
initWithTaskPool?: boolean | 是否使用taskpool进行多线程初始化,默认为开启 |
2.2 HMRouterPathInfo类
路由参数,用于路由跳转/返回参数。
参数 | 说明 |
---|---|
navigationId? | 根页面名称,对应的是Navigation的名称/ID,当navigationId为null时,表示为对最近一次navigation组件内进行路由跳转。 |
pageUrl? | 路由页面名称,对应的是NavDestination的名称,@HMRouter(pageUrl, isRegex, regexPriority, dialog, interceptor, animator, lifecycle)中的pageUrl,push时必须传入,否则会打印错误日志 |
param? | 传递的参数,当调用push时表示传递给下页面的参数对象,当调用pop时表示回传给上一页面的返回参数对象。 |
animator? | 自定义动画,传入使用此自定义动画执行转场,不再使用原先定义的转场。如果为false时,则不触发动画 |
interceptors? | 自定义拦截器,最高优先级执行。 |
lifecycle? | 自定义生命周期,最高优先级执行。 |
skipAllInterceptors? | 是否跳过所有拦截器,boolean类型 |
2.3 HMRouterPathCallback类
提供路由完成回调
参数 | 说明 |
---|---|
onResult | 页面返回回调,回调参数类型HMPopInfo |
onArrival | 目标页面跳转完成回调 |
onLost | 目标页面找不到回调 |
onResult回调可以在每次返回该页面时触发
2.4 HMPopInfo类
参数 | 说明 |
---|---|
srcPageInfo | name:返回的源页面,param: 源页面参数 |
info | 系统NavPathInfo对象 |
result | 返回携带的参数 |
2.5 HMParamType枚举
获取页面参数接口传参类型
枚举值 | 说明 |
---|---|
all | 接口返回HMPageParam对象,包含页面所有参数,HMPageParam.data为路由跳转时传入的param参数, HMPageParam.paramsMap为解析url路径参数,包含pathParam和queryParam内容 |
urlParam | 接口返回Map<string, Oject>对象,内容通过解析url获取,例如pageUrl定义/path/id,路由跳转时参数/path/id?name=xxx,则map包含name=123 |
routeParam | 接口返回Object对象,接口返回内容为路由时传入的HMRouterPathInfo的param对象 |
2.6 HMPageParam类
获取页面参数接口返回值类型
属性 | 说明 |
---|---|
data | Object或者null类型,为路由时传入的HMRouterPathInfo的param对象 |
urlParam | 接口返回Map<string, Object>对象,解析路由时pageUrl路径获取参数 |
2.7 HMPageInstance类
用于动态注册路由信息
参数 | 说明 |
---|---|
builder | 路由页面内容,需要使用NavDestination组件包裹,WrappedBuilder类型 |
pageUrl | 路由页面名称, 同@HMRouter pageUrl |
interceptorArray? | 页面对应拦截器,同@HMRouter interceptors |
singleton? | 页面是否单例,同@HMRouter singleton |
2.8 HMPageLifecycle类
@Entry页面生命周期管理类
方法 | 说明 |
---|---|
onDisAppear | 页面销毁时触发,绑定页面销毁时回调 |
onShown | 页面显示时触发,绑定页面显示时回调 |
onHidden | 页面隐藏时触发,绑定页面隐藏时回调 |
onBackPressed | 页面侧滑返回时触发,绑定页面侧滑返回时回调 |
六、部分案例介绍
1、页面跳转与返回
HMRouter提供了基于自定义注解的页面跳转与返回功能,使用步骤如下:
-
为需要跳转的页面添加@HMRouter注解,并配置其中的pageUrl参数,例如此处配置为ProductContent。
@HMRouter({ pageUrl: 'ProductContent' }) @Component export struct ProductContent {// ...@State param: ParamsType | null = null;aboutToAppear(): void {this.param = HMRouterMgr.getCurrentParam() as ParamsType;}// ... }
-
在需要进行页面跳转的位置,使用HMRouterMgr提供的新版链式APIto方法进行页面跳转,在参数中配置目标页面的pageUrl,param参数等,例如下述代码配置pageUrl为ProductContent,并传递了相关参数。此处也可以配置页面栈唯一标识navigationId,当使用多个HMNavigation时建议开发者手动指定,当使用单个HMNavigation时,开发者可以不传递navigationId参数,系统会默认处理。同时HMRouter对to方法还做了增强,可以在后面拼接传递多个参数,在其中配置返回到当前页面时数据接受的回调函数onResult,只要有页面返回到该页面时都会触发该函数,该函数会接收一个参数,可以通过该参数上的srcPageInfo.name获取到由哪个页面跳转到当前页,还可以从该参数上的result属性获取到其他页面pop到当前页面时传递的参数。
HMRouterMgr.to('ProductContent').withNavigation('mainNavigationId').withParam({ a: 1, b: 2 }).onResult((popInfo: HMPopInfo) => {const pageName = popInfo.srcPageInfo.name;const params = popInfo.result;console.log(`page name is ${pageName}, params is ${JSON.stringify(params)}`);}).pushAsync()
-
在跳转的目标页面使用HMRouterMgr.getCurrentParam()获取到传递的页面参数。
@Component export struct ProductContent {// ...@State param: ParamsType | null = null;aboutToAppear(): void {this.param = HMRouterMgr.getCurrentParam() as ParamsType;}// ... }
-
如需使用页面返回功能,在对应的业务逻辑位置使用HMRouterMgr提供的pop方法实现页面返回,同样的pop方法支持传入navigationId,同时HMRouter还支持在返回时通过配置param参数向其所返回的页面传递参数。
HMRouterMgr.pop({ navigationId: 'mainNavigationId', param: this.param })
2、多次页面跳转,返回指定页面
当页面跳转路径如HomePage->PageA->PageB->PageC,开发者希望在PageC的页面逻辑中直接返回到HomePage并携带参数,开发者仅需使用HMRouterMgr提供的pop方法,传入要返回目标页面的pageUrl、传递的参数param,即可直接带参返回到指定页面。
HMRouterMgr.to('MainPage') .withNavigation('mainNavigationId') .withParam(0) .pushAsync()
图1 返回指定页面示意图
3、应用未登录,点击跳转登录页的校验场景
应用中经常会有当用户未登录应用时,点击某些应用内容会自动跳转到登录页面的场景,在使用HMRouter对此场景进行实现时,可以采用以下步骤:
-
定义拦截器类LoginCheckInterceptor实现IHMInterceptor接口。
-
为定义的拦截器类添加@HMInterceptor注解,通过interceptorName配置拦截器名称LoginCheckInterceptor。
-
实现IHMInterceptor的intercept异步拦截器方法,在该方法中根据当前的登录状态来控制页面跳转的目标。
-
当用户已登录,通过执行chain.onContinue(),正常执行后续页面跳转逻辑。
-
当用户未登录,通过Toast弹窗向用户提示登录,然后跳转到登录页面,最后通过执行chain.onIntercept()来拦截此次跳转请求。
@HMInterceptor({ interceptorName: 'LoginCheckInterceptor' }) export class LoginCheckInterceptor implements IHMInterceptor {async intercept(chain: IHMInterceptorChain): Promise<void> {const info = chain.getRouterInfo();const context = chain.getContext();// ...if (!!AppStorage.get('isLogin')) {await chain.onContinue()} else {info.context.getPromptAction().showToast({ message: '请先登录' });HMRouterMgr.push({pageUrl: 'loginPage',skipAllInterceptor: true});await chain.onIntercept();}// ...} }
-
-
在需要进行拦截的页面中配置@HMRouter的interceptors参数即可,由于一个页面可以配置多个拦截器,所以需要将关联的拦截器名称封装为一个数组进行传入。
运行效果如下图所示。
4、实现单例页面的跳转
当应用中存在初始化加载资源消耗大且有复用需求的页面时,就可以使用单例页面。典型的业务场景如视频类应用中的视频播放页面,此类页面通常需要加载视频解码器资源并对其初始化,且该页面在视频类应用中会频繁出现。实现上开发者只需要配置@HMRouter注解参数中的singleton参数为true即可。
@HMRouter({ pageUrl: 'liveHome', singleton: true, animator: 'liveInteractiveAnimator', lifecycle: 'liveHomeLifecycle'})@Componentexport struct LiveHome { // ...}
5、实现弹窗类型的页面
在HMRouter路由框架中,开发者只需要设置@HMRouter注解的dialog配置为true即可将当前页面作为弹窗使用。
@HMRouter({ pageUrl: 'privacyDialog', dialog: true })
@Component
export struct PrivacyDialogContent {// ...
}
6、返回时弹窗,提示用户是否确认返回
当从某些页面返回时,应用希望通过弹窗方式让用户确认是否要执行返回操作,例如在订单支付页面中用户执行返回操作时,通常会弹窗提示用户是否确认退出,当用户点击确认后才会执行页面退出逻辑,此场景下就可以考虑使用弹窗类型页面加上自定义生命周期来实现。操作步骤如下:
-
开发者首先需要根据自己的业务需求,来进行自定义弹窗的开发。
@HMRouter({ pageUrl: 'PayCancel', dialog: true,animator:'PayCancelDialog' }) @Component export struct PayCancel {// ...build() {Stack({ alignContent: Alignment.Center }) {ConfirmDialog({title: '取消订单',content: '您确认要取消此订单吗?',leftButtonName: '再看看',rightButtonName: '取消订单',leftButtonFunc: () => {HMRouterMgr.popAsync({navigationId: this.queryNavigationInfo()?.navigationId});},rightButtonFunc: () => {// ...}});}.width('100%').height('100%').position({x: '50%',y: '50%'}).markAnchor({x: '50%',y: '50%'});} }
-
定义ExitPayLifecycle类来实现IHMLifecycle接口,为ExitPayLifecycle加上@HMLifecycle注解,传入生命周期名称ExitPayLifecycle,在类的内部,重写onBackPressed回调函数,当用户执行返回操作时,该回调函数触发,弹出刚刚定义的PayCancel弹窗。
@HMLifecycle({ lifecycleName: 'ExitPayLifecycle' })export class ExitPayLifecycle implements IHMLifecycle { model: ObservedModel = new ObservedModel();onBackPressed(): boolean { HMRouterMgr.to('PayCancel') .withParam(this.model.pageUrl) .pushAsync() return true; }}
-
将定义的生命周期与支付页面绑定,只需要将刚刚定义的生命周期传入对应组件@HMRouter注解的lifecycle参数即可。
@HMRouter({ pageUrl: 'PayDialogContent', dialog: true, lifecycle: 'ExitPayDialog', interceptors: ['LoginCheckInterceptor']})@Componentexport struct PayDialogContent { // ...}
运行效果如下图所示。
7、首页两次返回退出应用
该场景下用户第一次触发应用返回退出时向用户提示“再次返回退出”,第二次用户触发返回操作时应用真正退出。实现上可参考以下步骤:
-
定义一个生命周期类ExitAppLifecycle实现IHMLifecycle接口。
-
使用@HMLifecycle注解传入生命周期名称参数lifecycleName为ExitAppLifecycle。
-
重写其中的onBackPressed方法(此处是由于上述业务场景需要,实际开发中根据实际业务场景按需重写方法),通过判断上次返回操作与当前返回操作的时间间隔,按如下逻辑处理:
-
当两次返回操作的时间间隔大于设置值时(此处为1000ms),重新弹窗对用户进行提示,此处返回true,表示不执行默认返回逻辑。
-
当两次返回操作的时间间隔小于设置值时(此处为1000ms),返回为false表示执行默认返回逻辑,退出应用。
@HMLifecycle({ lifecycleName: 'ExitAppLifecycle' }) export class ExitAppLifecycle implements IHMLifecycle {private lastTime: number = 0;onBackPressed(ctx: HMLifecycleContext): boolean {let time = new Date().getTime();if (time - this.lastTime > 1000) {this.lastTime = time;ctx.uiContext.getPromptAction().showToast({message: 'Return to exit the application again.',duration: 1000});return true;} else {return false;}} }
-
-
将定义好的生命周期类与页面进行关联,开发者只需在@HMRouter注解中配置lifecycle为要关联的生命周期名称即可。
运行效果如下图所示。