HarmonyOS-ArkUI Navigation (导航组件)-第一部分
导航组件主要实现页面间以及组件内部的界面跳转,支持在不同的组件间进行参数的传递,提供灵活的跳转栈操作,从而便捷的实现对不同页面的访问和复用。
我们之前学习过Tabs组件,这个组件里面也有支持跳转的方式,Navigation与Tabs最主要的区别就在于,Navigation里面含有一个跳转栈,且使用方式比Tabs更为复杂。这个相当于Android中Fragment的Navigation,异曲同工。下文中我们将会习得
- 导航组件的显示模式
- 导航组件的路由操作
- 子页面管理
- 跨包跳转
- 跳转动效
想要支持Navigation的栈式维护和跳转,需要在页面的build函数下,将Navigation
作为根节点,也就是视图的根容器!要不然不行。并且,其跳转到的页面的根容器,一定要是NavDestination
,否则即使跳转过去了,您的页面也是没有被渲染的,Navigation组件本身也提供了布局的结构,也可以供您进行设置。
Navigation体系的界面有两大部分构成
- 主页, 主页是所有导航的源头处,引发的第一个导航。我们的路由栈(
NavPathStack
)也是在这个入口处进行设置,并由子页一层一层传递始终能够拿到这个句柄,来进行导航的。 - 子页, 就是被导航的页面。 子页也可以导航到其他的页面,主要就是得拿到那个唯一的路由栈(
NavPathStack
)。
主页与子页之间的导航器,可以配合由 Provide 和 Consume 两个装饰器来进行传递,之后有案例,可以在代码中注意一下使用方式。
主页与子页内容构成图
未加任何复杂操作的主页(主要展示标题,菜单栏,工具栏的展示方式)
@Entry
@Component
struct NavigatePage {@State message: string = 'Hello World';@State TooTmp:ToolbarItem = {value: 'func', icon: $r('app.media.ic_ok')}// 用这个@Provide装饰器装饰的变量,可以传递给子组件@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()build() {Column(){Navigation(this.pageInfos) {}//设置为分栏模式.mode(NavigationMode.Stack)//设置顶部标题.title("主标题")// 设置顶部菜单栏.menus([{value: "菜单1", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单2", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单3", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单4", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单5", icon: $r('app.media.ic_icon'), action: () => {}}])//设置底部工具栏.toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp])}.height('100%').width('100%').backgroundColor('#F1F3F5')}
}
界面设置基础
设置页面显示模式
在上文中的代码中,有一个属性叫 mode
, 它是用来设置页面模式的。 页面模式总共分为三种
- Stack:单栏
- Split: 分栏
- Auto:自适应
Split模式
上图中我们设置的主页面是 Stack模式,为单栏的效果。其实也可以设置分栏模式,其值设置为 Split即可。
下图为设置为分栏模式的样子,好让大家有个印象。
@Entry
@Component
struct NavigatePage {@State message: string = 'Hello World';@State TooTmp:ToolbarItem = {value: 'func', icon: $r('app.media.ic_ok')}// 用这个@Provide装饰器装饰的变量,可以传递给子组件@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()build() {Column(){Navigation(this.pageInfos) {}//设置为分栏模式.mode(NavigationMode.Split)//设置顶部标题.title("主标题")...省略其他设置代码...}.height('100%').width('100%').backgroundColor('#F1F3F5')}
}
Auto模式
Auto模式是让导航页面自己计算大小,屏幕宽就双列展示,不宽就单列显示。
设置标题模式
如果您写了一个Navigation
组件,没有设置title的情况下这个title是不会显示的。但是如果设置了,从上图中你也看到了,这个标题是不是有些,太大了🥶。Navigetion组件可以通过titleMode
属性设置标题栏的模式。titleMode
属性的值为:
- Free:默认值,我试着是和Full是一样的效果,官网API没看出来啥区别。遇见区别之后再补修一下文章。
- Full:强调型标题栏,用于以及界面需要图书标题的场景.
- Mini:普通标题栏模式,用于一级页面不需要突出标题的场景。
Full模式
mini模式
设置菜单栏
菜单栏的展示行为
菜单栏位置是下方红圈中区域。
当设备处于竖屏模式下,最多展示3个图标,为横屏模式下,最多展示5个图标。这里需要注意的是,这个展示的情况是与设备所处的横竖屏状态有关。
设置菜单栏
菜单栏通过Navigation组件的menus
属性进行设置。其声明格式如下:
menus(value: Array<NavigationMenuItem> | CustomBuilder): NavigationAttribute;
其参数可以使一个数组,也可以是一个自定义构建函数。
采用数组设置menus
build() {Column(){Navigation(this.pageInfos) {TextInput({placeholder: 'search'}).width("90%").margin({top: 20,}).backgroundColor('#FFFFFF')List({space: 20}) {ForEach(this.arr, (item:number) => {ListItem() {Text("Page" + item).width("100%").height(72).backgroundColor('#FFFFFF').borderRadius(24).fontSize(16).fontWeight(500).textAlign(TextAlign.Center).onClick(() => {this.pageInfos.pushPath({name: "NavDestinationTitle" + item})})}}, (item:number) => item.toString())}.width('90%').margin({top: 12})}//设置为分栏模式.mode(NavigationMode.Stack).navDestination(this.pageMap)//设置顶部标题.title("主标题").titleMode(NavigationTitleMode.Full)// 设置顶部菜单栏.menus([{value: "菜单1", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单2", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单3", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单4", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单5", icon: $r('app.media.ic_icon'), action: () => {}}])//设置底部工具栏.toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp])}.height('100%').width('100%').backgroundColor('#F1F3F5')}
采用自定义构建函数设置menus
@Builder myMenus(){Row(){Text('菜单1')Text('菜单2')Text('菜单3')Text('菜单4')Text('菜单5')}}build() {Column(){Navigation(this.pageInfos) {TextInput({placeholder: 'search'}).width("90%").margin({top: 20,}).backgroundColor('#FFFFFF')List({space: 20}) {ForEach(this.arr, (item:number) => {ListItem() {Text("Page" + item).width("100%").height(72).backgroundColor('#FFFFFF').borderRadius(24).fontSize(16).fontWeight(500).textAlign(TextAlign.Center).onClick(() => {this.pageInfos.pushPath({name: "NavDestinationTitle" + item})})}}, (item:number) => item.toString())}.width('90%').margin({top: 12})}//设置为分栏模式.mode(NavigationMode.Stack).navDestination(this.pageMap)//设置顶部标题.title("主标题").titleMode(NavigationTitleMode.Full)// 设置顶部菜单栏.menus(this.myMenus)//设置底部工具栏.toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp])}.height('100%').width('100%').backgroundColor('#F1F3F5')}
设置工具栏
工具栏是下图中所示的位置,如果不进行设置,就不会展示。工具栏的设置由toolBarConfiguration
属性进行设置。
toolBarConfiguration的声明如下文所示:
toolbarConfiguration(value: Array<ToolbarItem> | CustomBuilder, options?: NavigationToolbarOptions): NavigationAttribute;
其参数可以是一个数组,也可以是一个自定义构建函数。具体写法与menus非常类似。
@State TooTmp:ToolbarItem = {value: 'func', icon: $r('app.media.ic_ok')}build() {Column(){Navigation(this.pageInfos) {TextInput({placeholder: 'search'}).width("90%").margin({top: 20,}).backgroundColor('#FFFFFF')List({space: 20}) {ForEach(this.arr, (item:number) => {ListItem() {Text("Page" + item).width("100%").height(72).backgroundColor('#FFFFFF').borderRadius(24).fontSize(16).fontWeight(500).textAlign(TextAlign.Center).onClick(() => {this.pageInfos.pushPath({name: "NavDestinationTitle" + item})})}}, (item:number) => item.toString())}.width('90%').margin({top: 12})}//设置为分栏模式.mode(NavigationMode.Stack).navDestination(this.pageMap)//设置顶部标题.title("主标题").titleMode(NavigationTitleMode.Full)// 设置顶部菜单栏.menus(this.myMenus)//设置底部工具栏.toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp])}.height('100%').width('100%').backgroundColor('#F1F3F5')}
路由操作
我们在Navitgation构建的时候,传递了一个参数。如图所示:
上图中NavPathStack
是我们这个小节学习的重点,这个对象必须在Navigation构建的时候传入。Navigation整个体系下所有和路由相关的操作都是由NavPathStack
提供的方法进行,这个对象主要就是用于页面的管理。其功能主要如下:
- 页面跳转
- 页面返回
- 页面替换
- 页面删除
- 参数获取,数据传递。
- 路由拦截
- ...
我们在页面进行操作之前,是有必要稍微了解下,导航组件完成完整功能的构成成分。以及与我们之前写的代码的对应部分(红色虚线)。好在心里有个蓝图。
页面跳转的一切基础,就是首先Navigation对应的页面栈NavPathStack
您得提供。这个对象会在Navigation初始化的时候被传入,您可以用ArkUI中已经提供的类,也可以自己写一个子类,创建一个对象实例,传入即可。
@Entry
@Component
struct Index {// 创建一个页面栈对象并传入NavigationpageStack: NavPathStack = new NavPathStack()build() {Navigation(this.pageStack) {}.title('主标题')}
}
页面跳转
NavPathStack
通过Push相关的接口去实现页面跳转的功能,主要分为三类
- 普通跳转
- 带返回回调的跳转
- 带错误码的跳转,跳转结束会触发异步回调,返回错误码信息。
普通跳转
this.pageInfos.pushPath({name: "NavDestinationTitle" + item, param: ''})
this.pageInfos.pushPathByName("NavDestinationTitle" + item, '')
带返回回调的跳转
跳转时可以添加onPop回调,能在页面出栈时获取返回信息。不知道为什么我自己写的不会返回。这个我调试的表现跟官网不一样。
this.pageInfos.pushPathByName("NavDestinationTitle" + item, '', (popInfo) => {hilog.info(DOMAIN_NAVIGATE, TAG, 'Pop page name is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result))
})
带错误码的跳转
跳转结束触发异步回调,返回错误码信息。当被导航的界面打开的时候就会回调success
this.pageInfos.pushDestination({name: "NavDestinationTitle" + item, param: ''}).catch((error: BusinessError) => {hilog.error(DOMAIN_NAVIGATE, TAG, 'error Name= ' + error.name + " code=" + error.code + " message=" + error.message + " data=" + error.data)}).then(()=> {hilog.info(DOMAIN_NAVIGATE, TAG, "open success")})
或者这样写也可以
this.pageInfos.pushDestinationByName( "NavDestinationTitle" + item, '').catch((error: BusinessError) => {hilog.error(DOMAIN_NAVIGATE, TAG, 'error Name= ' + error.name + " code=" + error.code + " message=" + error.message + " data=" + error.data)}).then(()=> {hilog.info(DOMAIN_NAVIGATE, TAG, "open success")})
})
页面返回
NavPathStack通过pop相关接口去实现页面返回功能。
@Component
export struct PageOne {@State message: string = 'PageOne';@Consume('pageInfos') pageStack: NavPathStackbuild() {// 注意这里必须加上NavDestination方法,否则导航有效果但是界面展示不出来.NavDestination(){RelativeContainer() {Text(this.message + ' content').id('PageOne').fontSize($r('app.float.page_text_font_size')).fontWeight(FontWeight.Bold).alignRules({center: { anchor: '__container__', align: VerticalAlign.Center },middle: { anchor: '__container__', align: HorizontalAlign.Center }}).onClick(() => {this.message = 'Welcome';})}.height('100%').width('100%').backgroundColor(Color.Red)}.title(this.message).onBackPressed(()=>{const popInfo = this.pageStack.pop(); //弹出栈顶元素console.log('pop' + '返回值' + JSON.stringify(popInfo))return true;})}
}
另外还有几个其他的接口
// 返回到上一页
this.pageStack.pop()
//返回到上一个pageOne页
this.pageStack.popToName('pageOne')
// 返回到索引为1的页面
this.pageStack.popToIndex(1)
// 返回到根首页,也就是主页
this.pageStack.clear()
页面替换
NavPathStack通过Replace相关接口去实现页面的替换功能。
.onBackPressed(()=>{// 注意此时的name,名称要是在RouterMap中注册的那些文字,否则找不到要替换哪一个的const info = this.pageStack.replacePath({name: "NavDestinationTitle1", param: ""})console.log('pop' + '返回值' + JSON.stringify(info))return true;})
// 将栈顶页面替换为PageOne
this.pageStack.replacePath({ name: "PageOne", param: "PageOne Param" })
this.pageStack.replacePathByName("PageOne", "PageOne Param")
// 带错误码的替换,跳转结束会触发异步回调,返回错误码信息
this.pageStack.replaceDestination({name: "PageOne", param: "PageOne Param"}).catch((error: BusinessError) => {console.error(`Replace destination failed, error code = ${error.code}, error.message = ${error.message}.`);}).then(() => {console.info('Replace destination succeed.');})
页面删除
NavPathStack通过Remove相关接口实现页面的删除。
// 删除栈中name为PageOne的所有页面
this.pageStack.removeByName("PageOne")
// 删除指定索引的页面
this.pageStack.removeByIndexes([1,3,5])
// 删除指定id的页面
this.pageStack.removeByNavDestinationId("1");
移动页面
移动页面指的是,移动栈中的某指定页面到栈顶的位置。
// 移动栈中name为PageOne的页面到栈顶
this.pageStack.moveToTop("PageOne");
// 移动栈中索引为1的页面到栈顶
this.pageStack.moveIndexToTop(1);
获取参数
NavPathStack中的获取参数是比较有意思的,因为它可以按照页面获取参数,比如您现在在pageTwo中,也是可以搞到pogeOne中的参数的。这个就会数据中心的实现提供了很多的便利。
NavPathStack提供了一系列get操作去获取参数。
// 获取栈中所有页面name集合
this.pageStack.getAllPathName()
// 获取索引为1的页面参数
this.pageStack.getParamByIndex(1)
// 获取PageOne页面的参数
this.pageStack.getParamByName("PageOne")
// 获取PageOne页面的索引集合
this.pageStack.getIndexByName("PageOne")
路由拦截
NavPathStack提供了setInterception
方法,用于设置Navigation跳转的拦截回调, 该方法需要传入一个NavigationInterception
对象,该对象包含三个参数。
willShow | 页面跳转前回调,此时可以修改操作栈中的行为 |
didShow | 页面跳转后回调,此时不可以修改操作栈中的行为。 |
modeChange | 单双栏显示发生变化时回调,主要是横竖屏切换的时候。 |
下方代码为,当跳转到pageTwo的时候,我们拦截,使其转向pageOne。
aboutToAppear(): void {this.pageInfos.setInterception({/*** willShow需要格外注意的是, 当A跳转至B, B又跳转至A的时候, A的willShow也会被调用的,因为A的确是即将被展示了.* 我们在理逻辑的时候,除了注意将要跳转的界面,也要注意将要返回的界面.*/willShow: (from: NavDestinationContext | NavBar, to: NavDestinationContext | NavBar, operation: NavigationOperation, isAnimated: boolean) => {// 当为导航主页的时候,这里的to是一个string类型,内容为 "navBar"if (typeof to === 'string') {let toStr:string = to as stringhilog.warn(DOMAIN_NAVIGATE, TAG, 'navigation home page! to=' + to)return}// 当为子导航页的时候,这里的to是NavDestinationContext类型// 修改操作栈,发现是PageTwo的跳转,就改成PageOnelet target: NavDestinationContext = to as NavDestinationContexthilog.info(DOMAIN_NAVIGATE, TAG, 'common page! to=' + JSON.stringify(target))if (target.pathInfo.name === 'NavDestinationTitle2') {target.pathStack.pop()target.pathStack.pushPath({name: 'NavDestinationTitle1'})}},didShow: (from: NavDestinationContext | NavBar, to: NavDestinationContext | NavBar, operation: NavigationOperation, isAnimated: boolean) => {hilog.debug(DOMAIN_NAVIGATE, TAG, "didShow callback")},modeChange: (mode: NavigationMode) => {hilog.debug(DOMAIN_NAVIGATE, TAG, "modeChange callback")}})}
子页面
子页面对应的是NavDestination
组件,是Navigation体系中子页面部分的根容器,用于承载子页面的一些特殊属性和生命周期等。它可以设置独立的菜单栏和标题栏等属性,这类属性的设置方法与Navigation是一致的。
NavDestination的页面显示模式
NavDestination设置页面显示模式的属性,也叫 mode
。通过设置mode属性,可以满足不同页面的诉求。
页面显示类型
- 标准类型:NavDestination的默认显示模式就是标准类型,此时mode = NavDestinationMode.STANDARD 。 标准类型的子页面生命周期跟随在NavPathStack页面栈中的位置变化而变化。
- 弹窗类型:NavDestination设置mode为 NavDestinationMode.DIALOG类型时,代表当前子页面是弹窗类型,此时整个NavDestination默认透明显示,并且它的消失和出现并不会影响下层标准类型的显示和生命周期,两者是可以同时显示的。 弹窗默认动效是从低向上平滑弹出。
@Preview
@Component
export struct PageFourDialog {@Consume('pageInfos') pageStack: NavPathStackbuild() {NavDestination(){RelativeContainer() {RelativeContainer(){Text("这是一个弹窗").id('title').fontSize(20).alignRules({top: {anchor: '__container__', align: VerticalAlign.Top},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End}}).margin({top: 80}).fontWeight(FontWeight.Bold).textAlign(TextAlign.Center)Button("退出").fontSize(18).alignRules({top: {anchor: 'title', align: VerticalAlign.Bottom},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End}}).width('30%').margin({top: 50}).onClick(()=>{this.pageStack.pop()})}.alignRules({top: {anchor: '__container__', align: VerticalAlign.Top},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End},bottom: {anchor: '__container__', align: VerticalAlign.Bottom}}).height('30%').width('80%').backgroundColor(Color.White).borderRadius(20)}.height('100%').width('100%').borderRadius(20)}.hideTitleBar(true) //关掉标题栏区域.backgroundColor('rgba(0, 0, 0, 0.3)').mode(NavDestinationMode.DIALOG) //指定为弹窗模式}
}
页面生命周期
页面监听和查询
为了方便组件与页面解耦,在NavDestination子页面定义的自定义组件可以通过全局方法监听或查询到页面的一些状态信息。
页面信息查询
自定义组件提供了quertNavDestinationInfo
方法。可以在NavDestination内部查询到当前页面的信息,返回值是NavDestination
,如果查询不到则返回undefined。
import { JSON } from "@kit.ArkTS"@Preview
@Component
export struct PageFourDialog {@Consume('pageInfos') pageStack: NavPathStackbuild() {NavDestination(){RelativeContainer() {RelativeContainer(){Text("这是一个弹窗").id('title').fontSize(20).alignRules({top: {anchor: '__container__', align: VerticalAlign.Top},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End}}).margin({top: 80}).fontWeight(FontWeight.Bold).textAlign(TextAlign.Center)BackButton()}.alignRules({top: {anchor: '__container__', align: VerticalAlign.Top},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End},bottom: {anchor: '__container__', align: VerticalAlign.Bottom}}).height('30%').width('80%').backgroundColor(Color.White).borderRadius(20)}.height('100%').width('100%').borderRadius(20)}.hideTitleBar(true) //关掉标题栏区域.backgroundColor('rgba(0, 0, 0, 0.3)').mode(NavDestinationMode.DIALOG) //指定为弹窗模式}
}@Component
struct BackButton{@Consume('pageInfos') pageStack: NavPathStacknavDInfo:NavDestinationInfo | undefined;aboutToAppear(): void {// navDInfo = {"navigationId":"","name":"NavDestinationTitle4","state":4,// "index":0,"param":"NavDestinationTitle4","navDestinationId":"0"}this.navDInfo = this.queryNavDestinationInfo();console.log("navDInfo = " + JSON.stringify(this.navDInfo))}build() {Button("退出").fontSize(18).alignRules({top: {anchor: 'title', align: VerticalAlign.Bottom},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End}}).width('30%').margin({top: 50}).onClick(()=>{this.pageStack.pop()})}
}
页面状态监听
通过observer.on('navDestinationUpdate')提供的注册接口可以注册NavDestination生命周期变化的监听,使用方式如下:
import { JSON } from "@kit.ArkTS"
import { uiObserver } from "@kit.ArkUI"@Preview
@Component
export struct PageFourDialog {@Consume('pageInfos') pageStack: NavPathStackaboutToAppear(): void {uiObserver.on('navDestinationUpdate', (data: NavDestinationInfo)=>{console.log("navDestinationUpdate data=" + JSON.stringify(data))})}
...
监听切换页面的行为
aboutToAppear(): void {uiObserver.on('navDestinationUpdate', (data: NavDestinationInfo)=>{console.log("navDestinationUpdate data=" + JSON.stringify(data))})// 页面切换状态回调uiObserver.on('navDestinationSwitch', this.getUIContext(), (data:uiObserver.NavDestinationSwitchInfo) =>{console.log('navDestinationSwitch data=' + JSON.stringify(data))})}
页面转场
Navigation默认提供了页面的转场动画,通过页面栈操作时会触发不同的转场效果。如果不想要,可以设置关闭系统转场动画,同时您也可以进行自定义转场,以及共享元素转场的能力。
关闭转场
关闭转场有关闭全局转场和开启子页面时临时设置关闭转场动画。首先将关闭全局的转场。用到了NavPathStack的disableAnimation
接口。
aboutToAppear(): void {this.pageInfos.disableAnimation(true)}
当设置关闭动画的时候,转场会显得很突兀🙃。根据特定需求谨慎设置吧。
临时设置关闭转场-单次关闭
NavPathStack中提供的Push, Pop, Replace等接口中可以设置animated
参数,默认为true,表示带有转场动画,如果您需要这次操作中没有动画,则设置为false即可。
pageStack: NavPathStack = new NavPathStack()this.pageStack.pushPath({ name: "PageOne" }, false)
this.pageStack.pop(false)
跨包动态路由
我们首先看看之前写的代码, 在主导航页的时候,顶部出现了子页的import语句:
这种方式我们称之为静态路由跳转,这种方式能完成功能,但是存在缺陷
- 耦合过多,如果页面不出现这些import语句,在编译执行的时候,就不用处理这些连带的资源。
- 正是因为上个原因,会导致首页加载时间过长。
动态路由设计的初衷旨在解决多模块(HAR/HSP)能够复用相同的逻辑,实现业务模块之间的解耦,同时支持路由功能的扩展与整合。
系统路由表
上文提到过,动态路由是要解决多模块复用,解耦。系统路由表借助了Stage模型配置文件机制,在配置文件中添加路由表信息。这样的话,我们在配置文件中录入的就是路由表信息,也就是表示路由信息的字符串配置。之后代码中打开页面,仅仅指出路由表中配置的那些信息,系统就会找解析好的配置信息,动态调用配置好的方法。这个时候再引入上文中提到的那些静态资源也不迟。从而实现了页面懒加载。
让配置文件知道路由表的存在
很简单,在每个模块的module.json5 添加您的路由表配置即可。 module.json5是Stage开发模型中定义好的配置文件,每一个模块在创建之初都会新建一个这样的文件。
{"module": {"name": "entry","type": "entry","description": "$string:module_desc","mainElement": "EntryAbility","deviceTypes": ["phone"],"deliveryWithInstall": true,"installationFree": false,"pages": "$profile:main_pages", //这个是我们设置Pages的配置"routerMap": "$profile:router_map", //这个是我们路由导航的文件"abilities": [...],}
}
创建路由文件
很简单,看您模块mian_pages页面在哪里放着,我们的router_map文件就在哪里放着。因为就是同样的套路,连引用方式都是一样的,都是$profile:xxxx
找到的。创建一个新文件如下所示:
完善路由配置文件
{"routerMap" : [{"name": "PageOne", "pageSourceFile": "src/main/ets/pages/PageOne.ets","buildFunction": "open","data": {"description": "this is PageOne"}}]
}
- name: 跳转页面名称
- pageSourceFile: 跳转目标页的包内路径,相对于src的路径。
- buildFunction: 跳转目标页的入口函数名称,必须要用@Builder 装饰器修饰,且位置位于所指文件顶层。才能找到
- data: 应用自定义字段,可以通过配置项读取接口,
getConfigRouteMap
获取
在代码文件中补全配置文件中提到的入口函数
跳转代码
最后将原来文件中那些没必要的import, 设置RoutMap属性的代码都删掉。就完成解耦了。
自定义路由表
链接中是自定义路由表的实现方式。
参考文章:
CommonAppDevelopment/common/routermodule/README_AUTO_GENERATE.md · HarmonyOS-Cases/Cases - Gitee.com
文档中心
相关文章:
HarmonyOS-ArkUI Navigation (导航组件)-第一部分
导航组件主要实现页面间以及组件内部的界面跳转,支持在不同的组件间进行参数的传递,提供灵活的跳转栈操作,从而便捷的实现对不同页面的访问和复用。 我们之前学习过Tabs组件,这个组件里面也有支持跳转的方式,Navigati…...
技术速递|为 .NET 的 AI 评估解锁新的可能性
作者:Wendy Breiding 排版:Alan Wang Microsoft.Extensions.AI.Evaluations 库旨在简化将 AI 评估流程集成到应用程序中的过程。它提供了一个强大的框架,用于评估您的 AI 应用程序并自动化评估其性能。 去年11月,我们发布了该库的…...
android studio调试aosp手机userdebug版本无法查看局部变量和参数问题如何解决?
背景: 平常系统开发过程中,经常需要对一些代码进行相关追踪,这个时候很多同学会使用马哥课程讲解的android studio直接进行调试的方法,但是近期有学员朋友在群里反馈它在调试过程中无法看到方法参数的值,局部变量值&a…...
【OCR】技术
OCR图像识别 一、OCR是什么二、Python中如何实现OCR1.简单应用 三、OCR的核心步骤1.图像预处理(提高识别准确率)2.文字识别3.输出结果 四、OCR到的应用场景五、注意事项六、扩展学习 此贴用来更新在工作中遇到的一些图片解析内容 一、OCR是什么 …...
数据库同步中间件PanguSync:如何跳过初始数据直接进行增量同步
某些用户在使用数据库同步中间件PanguSync时说,我不想进行初次的全量同步,我已经源备份还原到目标库了,两边初始数据一样,想跳过初始数据,直接进行增量同步,该怎么设置。 直接上干货,按如下步骤…...
ICLR 2025|华科OVTR:首次实现端到端开放词汇多目标跟踪,刷新性能SOTA!
OVTR 是一种新型的多目标跟踪(MOT)方法,它由华中科技大学的团队提出,并发表于 ICLR 2025。该方法不仅速度快、适应性强,还能在开放词汇场景下实现零样本跟踪。本文将从背景、创新点到实验细节,全面介绍 OVT…...
个人学习编程(3-29) leetcode刷题
最后一个单词的长度: 思路:跳过末尾的空格,可以从后向前遍历 然后再利用 while(i>0 && s[i] ! ) 可以得到字符串的长度, int lengthOfLastWord(char* s) {int length 0;int i strlen(s) - 1; //从字符串末尾开始//…...
JSP 与 JavaScript 动态网页开发的比较
本质区别 特性JSP (JavaServer Pages)JavaScript执行位置服务器端客户端(浏览器)主要功能生成HTML内容操作DOM、处理用户交互数据获取直接访问服务器资源(数据库等)需要通过AJAX/Fetch API获取SEO友好是(内容在服务器生成)否(内容可能由JS动态生成)首次加载完整HTML可能需要多…...
Vue下 Sortable 实现 table 列表字段可拖拽排序,显示隐藏组件开发
vue 开发table 列表时,需要动态调整列字段的顺序和显示隐藏 实现效果如图所示: vue 组件代码 <template><div style"width: 90%; margin: 0 auto;"><el-table :data"tableData" border"" ref"table…...
Apache Shiro 全面指南:从入门到高级应用
一、Shiro 概述与核心架构 1.1 什么是 Shiro? Apache Shiro 是一个强大且易用的 Java 安全框架,它提供了认证(Authentication)、授权(Authorization)、加密(Cryptography)和会话管…...
高速电路中的存储器应用与设计三
4 DDR2 SDRAM 介绍及其应用要点 1. DDR2 SDRAM 概述 DDR2(Double Data Rate 2,两倍数据速率,版本 2)SDRAM,是由 JEDEC 国际标准组织开发的、基于 DDR SDRAM 的、升级的存储技术。与 DDR SDRAM 相比,虽然其…...
AndroidStudio无法识别连接夜神模拟器
下载夜神模拟器: https://www.yeshen.com/ 启动之后发现AS关联不了夜神模拟器,需要做如下的操作。 1:复制配置文件进入夜神模拟器 adb 相关的更改: 开启的命令是: 端口启动 固定: 夜神模拟器ÿ…...
Go 语言标准库中database模块详细功能介绍与示例
Go语言的标准库 database/sql 提供了与 SQL 数据库交互的通用接口,但需要搭配具体的数据库驱动(如 MySQL、PostgreSQL 等)使用。以下是 database/sql 的核心方法及示例说明: 1. 连接数据库 sql.Open(driverName, dataSourceName)…...
ai-api-union项目,适配各AI厂商api
项目地址:alpbeta/ai-api-union 需求:实现兼容各大模型厂商api的流式对话和同步对话接口,本项目现兼容智谱、豆包、通义、通义版deepseek 设计 一个ChatController类对外暴露这两个接口,入参都为ChatRequest请求类,…...
进程间通信——信号量
进程间通信——信号量 目录 一、基本概念 1.1 概念 1.2 基本操作 1.3 相关函数 1.3.1 semget创建/获取 1.3.2 semop操作信号量 1.3.3 semctl初始化/删除 二、代码操作 2.1 不用PV的 2.2 用PV 的 2.2.1 a.c 2.2.2 b.c 2.2.3 sem.h 2.2.4 sem.c 一、基本概念 1.1…...
CSS 如何设置父元素的透明度而不影响子元素的透明度
CSS 如何设置父元素的透明度而不影响子元素的透明度 在 CSS 中,设置父元素的透明度(如通过 opacity 属性)会影响所有子元素的透明度,因为 opacity 是作用于整个元素及其内容的。如果想让父元素透明但不影响子元素的透明度&#x…...
SpringBean模块(一)定义如何创建生命周期
一、介绍 1、简介 在 Spring 框架中,Bean 是指由 Spring 容器 管理的 Java 对象。Spring 负责创建、配置和管理这些对象,并在应用程序运行时对它们进行依赖注入(Dependency Injection,DI)。 通俗地讲,Sp…...
2007-2019年各省地方财政一般公共服务支出数据
2007-2019年各省地方财政一般公共服务支出数据 1、时间:2007-2019年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区、年份、地方财政一般公共服务支出 4、范围:31省 5、指标说明:地方财政一般公共服务…...
S32K144外设实验(六):FTM输出单路PWM
文章目录 1. 概述1.1 时钟系统1.2 实验目的2. 代码的配置2.1 时钟配置2.2 FTM模块配置2.3 输出引脚配置2.4 API函数调用1. 概述 1.1 时钟系统 FTM的CPU接口时钟为SYS_CLK,在RUN模式下最高80MHz。模块的时钟结构如下图所示。 从上图中可以看出,FTM模块的功能时钟为SYS_CLK,…...
二层综合实验
拓扑图 实验要求 1.内网IP地址使用172.16.6.0/16分配 2.sw1和sW2之间互为备份 3.VRRP/STP/VLAN/Eth-trunk均使用 4.所有Pc均通过DHCP获取IP地址 5.ISP只能配置IP地址 6.所有电脑可以正常访问IsP路由器环回 实验思路 这是一个二层综合实验每当拿到一个实验看清楚要求之后都有…...
《深度剖析SQL之WHERE子句:数据过滤的艺术》
在当今数据驱动的时代,数据处理和分析能力已成为职场中至关重要的技能。SQL作为一种强大的结构化查询语言,在数据管理和分析领域占据着核心地位。而WHERE子句,作为SQL中用于数据过滤的关键组件,就像是一把精准的手术刀,…...
Python每日一题(7)
Python每日一题 2025.3.27 一、题目二、分析三、自己源代码四、deepseek答案五、源代码与ai分析 一、题目 question["""编写程序,生成包含20个随机数的列表,然后将前十个元素升序排列,后10个元素降序排列,并输出结果""" ]二、分析 今天本来写了…...
Linux命令大全:从入门到高效运维
适合人群:Linux新手 | 运维工程师 | 开发者 目录 一、Linux常用命令(每天必用) 1. 文件与目录操作 2. 文件内容查看与编辑 二、次常用命令(按需使用) 1. 系统管理与监控 2. 网络与通信 3. 权限与用户管理 三、…...
Xss复现
目录 前提: 1.什么是XSS 2.XSS 的三种主要类型 复现 第1关 第2关 前提: 1.什么是XSS XSS(跨站脚本攻击,Cross-Site Scripting) 是一种常见的 Web 安全漏洞,攻击者通过向网页注入恶意脚本ÿ…...
3.28学习总结
完成分割回文串的算法题,难点主要在如何去分割,靠什么去分割字符串 int a(char arr[]){int i,j;int lenstrlen(arr);for(i0,jlen-1;i<j;i,j--){if(arr[i]!arr[j]) return 0;}return 1;}char **path;int pathtop;char***reasult;int*ansize;int count…...
【MySQL基础】数据库及表基本操作
作为运维工程师,掌握MySQL的基础操作是日常工作的重要技能之一。本文将介绍MySQL中数据库和表的基本操作,帮助您快速上手或复习这些核心概念。 1 数据库基本操作 1.1 创建数据库 create database db_name; -- 指定字符集和排序规则 create database d…...
注意!ChatGPT 全新 AI 图像功能延迟对免费用户开放
2025 年 3 月 25 日,OpenAI 正式宣布在 ChatGPT 中推出基于 GPT-4o 模型的全新原生图像生成功能。 这一功能允许用户通过对话生成和编辑图像,支持从写实风格到插图风格的多种形式。OpenAI 首席执行官萨姆・奥特曼(Sam Altman)在社…...
玛哈特液压式精密矫平机——以精准压力,定义金属的绝对服从
板材应力不除,良率难升。液压式精密矫平机,凭借多级液压闭环技术AI动态补偿算法,攻克0.2mm超薄钛箔至65mm装甲钢板的矫平极限,平整度精度锁定0.012mm,残余应力≤3MPa,让金属从“形似平整”迈向“分子级稳定…...
HCIA【NAT】
目录 1 NAT产生背景 2 NAT作用 3 NAT分类 NAT(网络地址转换技术) 1 NAT技术产生背景 [1] IPV4地址不够用 [2] 处于IPV4到IPV6的一个过渡阶段,IPV6地址还没有完全普及 2 作用 [1] 解决IP地址不够用的问题 [2] 做公网和私网地址的转换 …...
Reactor 事件流 vs. Spring 事件 (ApplicationEvent)
Reactor 事件流 vs. Spring 事件 ApplicationEvent Reactor 事件流 vs. Spring 事件 (ApplicationEvent)1️⃣ 核心区别2️⃣ Spring 事件 (ApplicationEvent)✅ 示例:Spring 事件发布 & 监听1️⃣ 定义事件2️⃣ 发布事件3️⃣ 监听事件🔹 进阶&…...
Google开源机器学习框架TensorFlow探索更多ViT优化
一、在边缘设备优化ViTa 在边缘设备上优化 ViT(Vision Transformer)模型,主要目标是减少计算量、降低功耗、提升推理速度。以下是几种关键优化策略: 1.轻量级 ViT 变体 部分 ViT 变体专为边缘设备优化,包括…...
docker - compose up - d`命令解释,重复运行会覆盖原有容器吗
docker - compose up - d`命令解释,重复运行会覆盖原有容器吗 docker - compose up - d 是一个用于管理 Docker 容器的命令,具体含义如下: 命令含义: up:用于创建、启动并运行容器,会根据 docker - compose.yml 文件中定义的服务配置来操作。-d:表示以“分离模式”(det…...
火山dts迁移工具使用
登录后选择生态工具。(数据库传输服务DTS) 先选region 创建迁移任务 假设,mysql 选择专有网络(一般上云到火山都是专有网络) 【先选】结构迁移,全量,这些 【再选】迁移对象 (他们产研有bug,先…...
Tabby 一:如何在Mac配置保姆级教程(本地模型替换hugging face下载)
1. brew安装 mac需要先安装brew,如果本地已经安装过brew这一步可以忽略,遇到问题可以自己ai问 /bin/bash -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 可能遇到source .zprofile失败,因为…...
Go 语言标准库中time模块详细功能介绍与示例
以下是 Go 语言 time 模块的详细说明及示例,涵盖时间操作、定时器、时区处理等核心功能: 一、时间基础操作 1. 获取时间 // 当前本地时间 now : time.Now() fmt.Println(now) // 2023-08-04 15:30:45.123456 0800 CST// 构造指定时间 t : time.Date(20…...
做的一些实验
先在DRMPlane::Perform函数里,把PLANE_SET_SRC_RECT或者PLANE_SET_DST_RECT设置的DRMProperty::SRC_W DRMProperty::SRC_H或者DRMProperty::CRTC_W DRMProperty::CRTC_H设置为原来1/2,都无法启动Android界面。 后来思考了一下,Log里好像打印…...
计算机网络基础:网络流量工程与优化策略
计算机网络基础:网络流量工程与优化策略 一、前言二、网络流量工程基础2.1 网络流量工程的定义与目标2.2 网络流量的测量与分析2.2.1 常用的流量测量方法2.2.2 流量数据分析三、网络流量工程的优化策略3.1 链路负载均衡策略3.1.1 基于目的地址的负载均衡3.1.2 基于流量权重的负…...
Python入门学习笔记 - 从环境搭建到基础语法
一、前期准备工作 1. 系统环境配置 防火墙设置与网络连接检查 2. 网络查看 二、计算机基础知识 1. 硬件系统 2. 计算机系统组成 三、Python简介 1. Python的特点 2. Python安装注意事项 四、Python基础语法 1. 注释的分类 2. IDE使用技巧 字体大小随滚轮滑动设置 3.…...
重温:时间窗口与滑动步长的概念
核心概念 窗口大小(Window Size) 定义窗口包含的时间步数(如过去7天、24小时等)。例如,窗口大小为5时,每个窗口包含连续的5个时间点数据。 滑动步长(Step/Stride) 窗口每次向前移动…...
数据库——MySQL字符、日期函数
一、字符函数详解 1. CONCAT() - 字符串拼接 作用:连接多个字符串,类似Java中的号。 语法: CONCAT(string1, string2, ...) 示例: SELECT CONCAT(Hello, , World) AS greeting; -- 输出:Hello World 2. UPPER()…...
Vue3.5 企业级管理系统实战(十一):全屏切换组件
本篇主要探讨如何在导航栏(Navbar)中添加全屏切换按钮,并借助功能强大的 screenfull 插件,丝滑实现全屏切换功能,为用户打造更为便捷、流畅的交互体验。 1 安装插件 screenfull screenfull 是一个轻量级的 JavaScript…...
HAL_UARTEx_ReceiveToIdle_DMA 开启,但是无法进入空闲中断;
HAL_UART_Receive_IT HAL_UARTEx_ReceiveToIdle_DMA 解决措施,关闭HAL_UART_Receive_IT函数; 因为这个函数会开启start_receive 函数中断,这个函数 将标志位一直置busy,导致一直没有进入空闲中断设置;...
第30周Java分布式入门 分布式基础
分布式基础课程笔记 一、什么是分布式? 1. 权威定义 分布式系统定义为:“利用物理架构形成多个自治的处理元素,不共享主内存,通过发送消息合作”。 2. 核心解释 物理架构与处理元素 🌟 多台独立服务器/电脑&#x…...
【商城实战(82)】区块链赋能用户身份验证:从理论到源码实践
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...
一周掌握Flutter开发--9. 与原生交互(上)
文章目录 9. 与原生交互核心场景9.1 调用平台功能:MethodChannel9.1.1 Flutter 端实现9.1.2 Android 端实现9.1.3 iOS 端实现9.1.4 使用场景 9.2 使用社区插件9.2.1 常用插件9.2.2 插件的优势 总结 9. 与原生交互 Flutter 提供了强大的跨平台开发能力,但…...
DeepSeek-V3-0324对比OpenAI GPT-4o和Gemini 2.5 Pro
以下是DeepSeek-V3-0324、OpenAI GPT-4o与谷歌Gemini 2.5 Pro模型的更新点及优化对比总结: 1. DeepSeek-V3-0324 开源地址:https://huggingface.co/deepseek-ai/DeepSeek-V3-0324 核心更新与优化 性能提升: 采用6850亿参数MoE架构ÿ…...
【AI论文】LEGO拼图:大型语言模型在多步骤空间推理方面的表现如何?
摘要:多步骤空间推理涉及跨多个顺序步骤理解和推理空间关系,这对于解决复杂的现实世界应用至关重要,如机器人操作、自主导航和自动化装配。为了评估当前多模态大型语言模型(MLLMs)在获取这一基本能力方面的表现&#x…...
【深度学习】【目标检测】【OnnxRuntime】【C++】YOLOV5模型部署
【深度学习】【目标检测】【OnnxRuntime】【C】YOLOV5模型部署 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【目标检测】【OnnxRuntime】【C】YOLOV5模型部署前言Windows平台搭建依赖环境模型转换--pytorch转onnxONNXRuntime推…...
vcpkg安装指定版本的库
一.vcpkg安装 使用git将vcpkg源码克隆到本地制定目录(D:\vcpkg),并初始化 git clone https://github.com/microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh # Linux/macOS .\bootstrap-vcpkg.bat # Windows 如下图: 二.安…...
springboot 四层架构之间的关系整理笔记五
问题:service 和 多个serviceimpl 分层之间的逻辑关系? 好的!用班级活动的例子继续讲,假设班长(Service接口)要管理多种任务,而不同的班委(ServiceImpl实现类)负责不同场…...