想了解更多關(guān)于開源的內(nèi)容,請訪問:
51CTO 鴻蒙開發(fā)者社區(qū)
https://ost.51cto.com
頁面跳轉(zhuǎn)是路由最常用的能力,Navigation通過NavPathStack提供了諸多方法,下文以pushDestination方法為例,介紹Navigation的路由跳轉(zhuǎn)相關(guān)能力。
NavPathStack提供了路由管理的能力,通過NavPathStack進(jìn)行頁面跳轉(zhuǎn),主要適用于頁面較多的應(yīng)用。
Step1:創(chuàng)建NavPathStack對象pageStack,通常使用@Provide進(jìn)行修飾,方便后續(xù)子組件通過@Comsumer獲取,以實現(xiàn)子頁面的路由跳轉(zhuǎn)。
也可以將pageStack傳入路由框架,以實現(xiàn)路由框架開發(fā)(后續(xù)路由框架章節(jié)會介紹)的開發(fā)。
@Entry @Component struct mainPageView { @Provide('pageStack') pageStack: NavPathStack = new NavPathStack() ... build() { ... } }
Step2:構(gòu)建路由表pageMap,該方法通過@Builder進(jìn)行修飾,通過傳入的pageName屬性,返回不同頁面。
@Entry @Component struct mainPageView { @Provide('pageStack') pageStack: NavPathStack = new NavPathStack() @Builder PageMap(pageName: string) { if (pageName === 'loginPage') { loginPageView() } else if (pageName === 'mainPage') { mainPageView() } } build() { ... } }
Step3:在build創(chuàng)建Navigation組件(需要傳入pageStack參數(shù)),通過navDestination屬性傳入路由表pageMap,并通過pageStack.pushPath()實現(xiàn)頁面跳轉(zhuǎn)。
@Entry @Component struct mainPageView { @Provide('pageStack') pageStack: NavPathStack = new NavPathStack() @Builder pageMap(pageName: string) { if (pageName === 'loginPage') { loginPageView() } else if (pageName === 'mainPage') { mainPageView() } } build() { Navigation(this.pageStack){ ... Button('login').onClick( ent => { let pathInfo : NavPathInfo = new NavPathInfo('loginPage', null) this.pageStack.pushDestination(pathInfo, true); }) }.navDestination(this.pageMap) ... } }
Navigation的頁面間,通過NavPathInfo對象中的params屬性,實現(xiàn)從發(fā)起頁到目標(biāo)頁的數(shù)據(jù)傳遞;通過onPop回調(diào)參數(shù),實現(xiàn)處理目標(biāo)頁面的返回。
Step1:構(gòu)建NavPathInfo對象,輸入需要傳遞給目標(biāo)頁面的參數(shù)。
params參數(shù):將需要傳遞的數(shù)據(jù)封裝起來進(jìn)行傳遞,無法傳遞對象里面的函數(shù)。具體的支持參數(shù)可以參考指南()
onPop參數(shù):目標(biāo)頁面觸發(fā)pop時的返回,在回調(diào)中通過PopInfo.info.param獲取到返回的對象。
// 發(fā)起頁 mainPage let loginParam : LoginParam = new LoginParam() // 構(gòu)建pathInfo對象 let pathInfo : NavPathInfo = new NavPathInfo('loginPage', loginParam , (popInfo: PopInfo) => { let loginParam : LoginParam = popInfo.info.param as LoginParam; ... }) // 講參數(shù)傳遞到目標(biāo)頁 this.pageStack.pushDestination(pathInfo, true);
Step2:目標(biāo)頁通過“NavPathStack.getParamByIndex(0)”獲取到發(fā)起頁傳遞過來的參數(shù)
@Component export struct loginPageView { @Consume('pageInfo') pageStack : NavPathStack; aboutToAppear(): void { this.loginParam = this.pageStack.getParamByIndex(0) as LoginParam; } ... }
Step3:目標(biāo)頁通過NavPathStack.pop方法返回起始頁,其result參數(shù)用來傳遞需要返回給起始頁的對象。
@Component export struct loginPageView { @Consume('pageInfo') pageStack : NavPathStack; // 頁面構(gòu)建的對象 private loginParam! : LoginParam; ... build() { NavDestination(){ ... Button('login').onClick( ent => { // 將對象返回給起始頁 this.pageStack.pop(this.loginParam, true) }) } } }
當(dāng)應(yīng)用模塊較多,需要使用HSP(HAR)進(jìn)行多模塊開發(fā),比如登錄模塊是一個獨立團隊開發(fā),以HSP(HAR)的形式交付。此時主頁應(yīng)當(dāng)從mainPage跳轉(zhuǎn)到HSP(HAR)中的頁面,需要先導(dǎo)入模塊的自定義組件,將組件添加到pageMap中,再通過pushDestination進(jìn)行跳轉(zhuǎn)。
Step1:從HSP(HAR)中完成自定義組件(需要跳轉(zhuǎn)的目標(biāo)頁面)開發(fā),講自定義組件申明為export。
@Component export struct loginPageInHSP { @Consume('pageStack') pageStack: NavPathStack; ... build() { NavDestination() { ... } } }
Step2:在HSP(HAR)的index.ets中導(dǎo)出組件。
export { loginPageInHSP } from "./src/main/ets/pages/loginPageInHSP"
Step3:配置好HSP(HAR)的項目依賴后,在mainPage中導(dǎo)入自定義組件,并添加到pageMap中,即可正常調(diào)用。// 導(dǎo)入模塊目標(biāo)頁自定義組件。
import { loginPageInHSP } from 'library/src/main/ets/pages/loginPageInHSP' @Entry @Component struct mainPage { @Provide('pageStack') pageStack: NavPathStack = new NavPathStack() @Builder pageMap(name: string) { if (name === 'loginPageInHSP') { // 路由到hsp包中的登錄頁面 loginPageInHSP() } } build() { Navigation(this.pageStack) { Button("login With HSP module") .onClick(() => { let loginParam : LoginParamInHSP = new LoginParamInHSP() let pathInfo : NavPathInfo = new NavPathInfo('loginPageInHSP', loginParam, (popInfo: PopInfo) => {}) this.pageStack.pushDestination(pathInfo, true); }) } .navDestination(this.pageMap) } }
Navigation的pushXXX和pop方法中都帶有一個參數(shù)animated,將animated設(shè)置成false則會取消轉(zhuǎn)場動畫,路由到Dialog模式頁面或者路由出Dialog模式頁面是,均無轉(zhuǎn)場動畫,如果需要轉(zhuǎn)場動畫,可以通過自定義轉(zhuǎn)場動畫實現(xiàn)。
Navigation通過customNavContentTransition事件提供自定義轉(zhuǎn)場動畫的能力,當(dāng)轉(zhuǎn)場開始時,通過回調(diào)函數(shù)告知開發(fā)者,告知此次動畫from(從哪來)、to(到哪去)、是Push、Pop亦或是Repalce。這里需要注意當(dāng)為根視圖時,NavContentInfo的name值為undefined。
開發(fā)者可以在customNavContentTransition的回調(diào)函數(shù)中進(jìn)行動畫處理,返回NavigationAnimatedTransition自定義轉(zhuǎn)場協(xié)議已實現(xiàn)自定義轉(zhuǎn)場。
NavigationAnimatedTransition對象中包含三個參數(shù),timeout(動畫超時結(jié)束時間),transition(自定義動畫執(zhí)行回調(diào)),onTransitionEnd(轉(zhuǎn)場完成回調(diào)),需要在transition方法中實現(xiàn)具體動畫邏輯。
由于自定義轉(zhuǎn)場參數(shù)是在Navigation層級,但是每個頁面都會有其特定的自定義轉(zhuǎn)場效果,因此需要定義一套轉(zhuǎn)場動畫框架,已實現(xiàn)在Navigation層面對框架進(jìn)行統(tǒng)一管理,各個頁面通過實現(xiàn)框架提供的回調(diào)函數(shù),將其特定的動畫效果傳遞給Navigation。
Step1:構(gòu)建動畫框架,通過一個Map管理各個頁面自定義自定義動畫對象CustomTransition,CustomTransition對象提供了Push、Pop、Replace各個動畫階段的回調(diào)函數(shù)給各個頁面進(jìn)行補充,此處將各個階段細(xì)分為In和Out,從而實現(xiàn)頁面進(jìn)入和退出時不同的轉(zhuǎn)場效果。
// 自定義動畫對象,定義了Push、Pop、Replace各個動畫階段的回調(diào)函數(shù)。
export class CustomTransition { pageID : number = -1; onPushInStart: () => void = () => {}; onPushInEnd: () => void = () => {}; onPushInFinish: () => void = () => {}; onPopInStart: () => void = () => {}; onPopInEnd: () => void = () => {}; onPopInFinish: () => void = () => {}; onReplaceInStart: () => void = () => {}; onReplaceInEnd: () => void = () => {}; onReplaceInFinish: () => void = () => {}; onPushOutStart: () => void = () => {}; onPushOutEnd: () => void = () => {}; onPushOutFinish: () => void = () => {}; onPopOutStart: () => void = () => {}; onPopOutEnd: () => void = () => {}; onPopOutFinish: () => void = () => {}; onReplaceOutStart: () => void = () => {}; onReplaceOutEnd: () => void = () => {}; onReplaceOutFinish: () => void = () => {}; ... // 獲取啟動階段參數(shù)回調(diào) public getStart(operation : NavigationOperation, isInPage : boolean) : () => void { if (operation == NavigationOperation.PUSH) { if (isInPage) { return this.onPushInStart; } else { return this.onPushOutStart; } } else if (operation == NavigationOperation.POP) { if (isInPage) { return this.onPopInStart; } else { return this.onPopOutStart; } } else { if (isInPage) { return this.onReplaceInStart; } else { return this.onReplaceOutStart; } } } // 獲取動畫結(jié)束階段參數(shù)回調(diào) public getEnd(operation : NavigationOperation, isInPage : boolean) : () => void { ... } // 獲取動畫結(jié)束后參數(shù)回調(diào) public getFinished(operation : NavigationOperation, isInPage : boolean) : () => void { ... } } // 自定義動畫對象框架 export class CustomTransitionFW { // 各個頁面自定義動畫對象映射表 private customTransitionMap: Map<number, CustomTransition> = new Map<number, CustomTransition>() ... registerNavParam(ct : CustomTransition): void { ... this.customTransitionMap.set(ct.pageID, ct); } unRegisterNavParam(pageId: number): void { ... this.customTransitionMap.delete(pageId); } getAnimateParam(pageId: number): CustomTransition { ... return this.customTransitionMap.get(pageId) as CustomTransition; } }
Step2:配置Navigation的customNavContentTransition屬性,當(dāng)返回undefined時,使用系統(tǒng)默認(rèn)動畫。
build() { Navigation(this.pageStack){ ... }.hideTitleBar(true) .hideToolBar(true) .navDestination(this.pageMap) .customNavContentTransition((from: NavContentInfo, to: NavContentInfo, operation: NavigationOperation) => { // 對于Dialog型的頁面,此處統(tǒng)一做了自定義動畫的屏蔽,若需要動畫,可以不做此判斷。 if (from.mode == NavDestinationMode.DIALOG || to.mode == NavDestinationMode.DIALOG) { console.error(`==== no transition because Dialog`); return undefined; } let pageIn : CustomTransition | undefined; let pageOut : CustomTransition | undefined; pageIn = CustomTransitionFW.getInstance().getAnimateParam(to.index) pageOut = CustomTransitionFW.getInstance().getAnimateParam(from.index) // 業(yè)務(wù)首頁跳轉(zhuǎn)時若沒有自定義動畫訴求,此處可以通過判斷頁面id是否為-1(-1表示Navigation根視圖)進(jìn)行跳出。 if (from.index === -1 || to.index === -1) { return undefined; } // 創(chuàng)建自定義轉(zhuǎn)場協(xié)議,各個頁面都會根據(jù)協(xié)議中的配置進(jìn)行轉(zhuǎn)場,當(dāng)返回undefined時,使用系統(tǒng)默認(rèn)動畫。 let customAnimation: NavigationAnimatedTransition = { onTransitionEnd: (isSuccess: boolean)=>{ ... }, transition: (transitionProxy: NavigationTransitionProxy)=>{ ... }, timeout: 100, }; return customAnimation; }) }
Step3:customNavContentTransition事件需要返回NavigationAnimatedTransition對象,具體的動畫實現(xiàn)需要在NavigationAnimatedTransition的transition屬性中實現(xiàn)。transition中通過各個頁面在框架中注冊的回調(diào)函數(shù),配置框架需要的動畫屬性。案例中各個頁面注冊了PUSH/POP/REPLACE的各個階段動畫參數(shù)。此處需要注意由于Navigation根頁面不在棧中,因此無法與NavDestination無法產(chǎn)生跳轉(zhuǎn)聯(lián)動,因此如果第一個入棧的頁面也需要自定義動畫,那么就需要判斷pageId是否為-1(-1及表示為根視圖),如果是-1則不就行動畫設(shè)置。
let customAnimation: NavigationAnimatedTransition = { ... transition: (transitionProxy: NavigationTransitionProxy)=>{ // 配置起始參數(shù) if (pageOut != undefined && pageOut.pageID != -1) { pageOut.getStart(operation, false)(); } if (pageIn != undefined && pageIn.pageID != -1) { pageIn.getStart(operation, true)(); } // 執(zhí)行動畫 animateTo({ duration: 1000, curve: Curve.EaseInOut, onFinish: ()=>{ if (pageOut != undefined && pageOut.pageID != -1) { pageOut.getFinished(operation, false)(); } if (pageIn != undefined && pageIn.pageID != -1) { pageIn.getFinished(operation, true)(); } transitionProxy.finishTransition(); }}, ()=>{ if (pageOut != undefined && pageOut.pageID != -1) { pageOut.getEnd(operation, false)(); } if (pageIn != undefined && pageIn.pageID != -1) { pageIn.getEnd(operation, true)(); } }) } }
Step4:在各個頁面中定義動畫回調(diào),并往自定義動畫框架中注冊。并在組件onDisAppear生命周期中注銷框架中的頁面動畫回調(diào)。
Step5:定義NavDestination的translate屬性,已實現(xiàn)動畫效果。
@Component export struct loginPageView { ... private pageId: number = 0; @State transX: number = 0; @State transY: number = 0; aboutToAppear(): void { this.pageId = this.pageStack.getAllPathName().length - 1; let ct : CustomTransition = new CustomTransition(); ct.pageID = this.pageId; ct.onPushInStart = ct.onPushOutEnd = ct.onPopInStart = ct.onPopOutEnd = ct.onReplaceInStart = ct.onReplaceOutEnd = () => { this.transX = -300; } ct.onPushInEnd = ct.onPushOutStart = ct.onPopInEnd = ct.onPopOutStart = ct.onReplaceInEnd = ct.onReplaceOutStart = () => { this.transX = 0; } ct.onPushInFinish = ct.onPopInFinish = ct.onReplaceInFinish = () => { this.transX = 0; } ct.onPushOutFinish = ct.onPopOutFinish = ct.onReplaceOutFinish = () => { this.transX = -300; } // 將頁面的動畫效果注冊到動畫框架中 CustomTransitionFW.getInstance().registerNavParam(ct) } build() { NavDestination(){ ... }.hideTitleBar(true) .onDisAppear(()=>{ // 組件銷毀的時候,需要將頁面的動畫效果從框架中刪除 CustomTransitionFW.getInstance().unRegisterNavParam(this.pageId) }) // 定義translate,已實現(xiàn)動畫 .translate({x: this.transX, y: this.transY, z: 0}) } }
NavDestination之間可以通過geometryTransition實現(xiàn)共享元素轉(zhuǎn)場。
起始頁。
Step1:為需要實現(xiàn)共享元素轉(zhuǎn)場的元素添加geometryTransition屬性,id參數(shù)必須在兩個NavDestination之間保持一致。
起始頁代碼。
Column() { Image($r('app.media.startIcon')) .geometryTransition('1') Text("起始頁共享的圖片") } .width(100) .height(100)
目的頁代碼。
Column() { Image($r('app.media.startIcon')) .geometryTransition('1') Text("目的頁共享的圖片") } .width(200) .height(200)
Step2:animateTo方法發(fā)起頁面跳轉(zhuǎn)(push 或者 pop),觸發(fā)共享元素轉(zhuǎn)場動畫執(zhí)行。注意此處需要關(guān)閉頁面默認(rèn)的跳轉(zhuǎn)動畫。
Button('跳轉(zhuǎn)目的頁') .width('80%') .height(40) .margin(20) .onClick(() => { animateTo({ duration: 1000 }, () => { this.pageInfos.pushPath({ name: 'DestinationPage' }, false) }) })
想了解更多關(guān)于開源的內(nèi)容,請訪問:
51CTO 鴻蒙開發(fā)者社區(qū)
https://ost.51cto.com
本文鏈接:http://www.tebozhan.com/showinfo-26-87498-0.htmlNavigation常見場景的解決方案
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com