场景描述
透明页面也可以叫做弹窗页面,实际开发场景中经常有一个页面覆盖在另一个页面上的效果,例如:评论弹窗页面、广告弹窗页面等。
功能点:
方案一:使用router+subWindow实现
router路由无法更改页面模式,所以无法直接实现透明页面,需要借助拉起子窗口的方案实现透明页面的效果。思路如下:
- 获取窗口实例。
- 拉起一个子窗口并加载对应页面。
- 设置子窗口背景透明。
- 定义子窗口的关闭方案。
核心代码
在Ability中获取windowStage实例。
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
windowStage.loadContent('pages/Index', (err) => {
// 这里需要注意为了确保windowStage实例获取成功,我们最好在loadContent回调中回去,能保证页面加载成功的时候一定能讲windowStage实例存到AppStorage对象中
AppStorage.setOrCreate("windowStage", windowStage);
});
}
创建一个子窗口作为页面载体,并加载RouterOpacityPage页面。
private windowClass: window.WindowStage | null = null
aboutToAppear(): void {
this.windowClass = AppStorage.get("windowStage") as window.WindowStage;
}
build() {
...
Button("使用router路由")
.onClick(() => {
this.windowClass?.createSubWindow("routerOpacityPage", (err, win) => {
win.setUIContent('pages/RouterOpacityPage');
win.showWindow();
})
})
...
}
加载页面后,这时候出现的新页面发现并不是透明的,那么我们把页面跟容器设置背景颜色为透明,也没有效果,根因是窗口默认是不透明的,需要设置窗口背景色。
@Entry
@Component
struct RouterOpacityPage {
aboutToAppear(): void {
// 设置当前窗口背景透明
window.findWindow("routerOpacityPage").setWindowBackgroundColor("#00000000");
}
build() {
...
}
}
需要注意的是,子窗口无法与主窗口事件交互,并且默认的手势返回也无法销毁,所以需要自己监听页面的返回手势来销毁子窗口来实现回到原页面的效果。
onBackPress(): boolean | void {
// 这里解释下为什么需要用显示动画,因为窗口消失的时候无法对窗口添加动画,在转场动画中动画结束回调不生效,所以只能通过显示动画来控制组件显影然后在结束回调同销毁窗口
animateTo({
duration: 300, onFinish: () => {
window.findWindow("routerOpacityPage").destroyWindow().then((res) => {
console.log("destroyWindow success");
}).catch(() => {
console.log("destroyWindow fail");
})
}
}, () => {
this.opacityValue = 0;
})
return true;
}
RouterOpacityPage 完整代码如下:
import { window } from '@kit.ArkUI'
@Entry
@Component
struct RouterOpacityPage {
@State opacityValue: number = 1;
aboutToAppear(): void {
// 设置当前窗口背景透明
window.findWindow("routerOpacityPage").setWindowBackgroundColor("#00000000");
}
onBackPress(): boolean | void {
// 这里解释下为什么需要用显示动画,因为窗口消失的时候无法对窗口添加动画,在转场动画中动画结束回调不生效,所以只能通过显示动画来控制组件显影然后在结束回调同销毁窗口
animateTo({
duration: 300, onFinish: () => {
window.findWindow("routerOpacityPage").destroyWindow().then((res) => {
console.log("destroyWindow success");
}).catch(() => {
console.log("destroyWindow fail");
})
}
}, () => {
this.opacityValue = 0;
})
return true;
}
build() {
Column() {
Column() {
Text("页面2").fontSize(50).fontWeight(FontWeight.Bold)
}
.backgroundColor(Color.White)
.borderRadius(20)
.width("80%")
.height("60%")
.justifyContent(FlexAlign.Center)
}
.opacity(this.opacityValue)
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
.backgroundColor("#60000000")
.transition(TransitionEffect.OPACITY.animation({ duration: 300 }))
}
}
以上使用subWindow的方案实现了一个简单的透明页面效果,实际场景中可能还涉及到页面的持久化与参数传递。
页面持久化方案
上面代码中,我们在退出页面的时候使用的window.destroyWindow()方法,会导致整个窗口实例销毁,无法保存页面中的状态,这里我们需要使用window.minimize()方法来隐藏子窗口,而不是销毁子窗口,相关代码如下:
import { window } from '@kit.ArkUI';
import CommentComponent from '../component/CommentComponent';
@Entry
@Component
struct OpacityPage {
@State opacityValue: number = 1;
@State initialIndex: number = 0;
onBackPress(): boolean | void {
this.closeSubWindow();
return true;
}
onPageShow(): void {
this.opacityValue = 1;
}
closeSubWindow() {
animateTo({
duration: 300, onFinish: () => {
// 当转场动画结束的时候执行窗口隐藏效果,注意这里不能使用destroyWindow销毁当前窗口,因为窗口销毁会导致page状态消失
window.findWindow("OpacityPage").minimize().then((res) => {
console.log("minimizeWindow success");
}).catch(() => {
console.log("minimizeWindow fail");
})
}
}, () => {
this.opacityValue = 0;
})
}
build() {
Column() {
Column() {
CommentComponent({ initialIndex: this.initialIndex })
}
.backgroundColor(Color.White)
.borderRadius(20)
.width("80%")
.height("60%")
.justifyContent(FlexAlign.Center)
.onClick(() => {})
}
.opacity(this.opacityValue)
.animation({ duration: 300 })
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
.backgroundColor("#60000000")
.transition(TransitionEffect.OPACITY.animation({ duration: 300 }))
.onClick(() => {
this.closeSubWindow()
})
}
}
实现效果如下所示:
页面参数传递方案
因为窗口之前没有提供数据传递的API,所以无法直接传递页面参数;但是每个窗口都有自己的UIContext,可以通过UIContext获取其他窗口的router路由栈,并进行参数传递操作,但是因为该方案会造成不必要是内存消耗,影响性能,并且实现起来较复杂这里只提供思路,不做具体实现,相关功能在navigation路由中实现。
方案二:使用DIALOG类型NavDestination实现【推荐】
使用navigation作为路由框架时,实现透明页面只需要设置页面的NavDestinationMode属性为DIALOG模式思路如下:
- 使用navigation作为页面跟容器。
- 跳转NavDestination页面并设置其mode属性为NavDestinationMode.DIALOG。
- 添加自定义转场动画。这里使用的组件转场,可根据实际需要替换为navigation的自定义转场。
核心代码
使用navigation作为跟页面容器。
`import { window } from '@kit.ArkUI';
@Entry
@Component
struct Index {
pageInfos: NavPathStack = new NavPathStack();
build() {
Navigation(this.pageInfos) {
Column({ space: 8 }) {
Button("使用navigation路由")
.onClick(() => {
this.pageInfos.pushPath({ name: 'RouterOpacityPage2' });
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
.height('100%')
.width('100%')
.hideTitleBar(true)
.hideBackButton(true)
.hideToolBar(true)
}
}
子页面设置当前的页面模式为DIALOG模式。
@Builder
export function RouterOpacityPage2Builder(name: string, param: Object) {
RouterOpacityPage2();
}
@Component
export struct RouterOpacityPage2 {
@State opacityValue: number = 1;
pageInfos: NavPathStack = new NavPathStack();
build() {
NavDestination() {
Column() {
Column() {
Text("页面2").fontSize(50).fontWeight(FontWeight.Bold)
}
.width('80%')
.height('60%')
.backgroundColor(Color.White)
.borderRadius(20)
}
.width("100%")
.height("100%")
.backgroundColor("#60000000")
.justifyContent(FlexAlign.Center)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
.opacity(this.opacityValue)
// 这里的动画可以使用navigation的自定义转场来实现,这里主要针对透明页面效果,动画效果不深入探讨实现
.transition(TransitionEffect.OPACITY.animation({ duration: 300 }))
}
// 这里设置当前页面模式为DIALOG模式,默认情况下DIALOG模式就是透明页面
.mode(NavDestinationMode.DIALOG)
.hideTitleBar(true)
.onBackPressed(() => {
// 与第一种实现方式一样,这里也是用显示动画实现消失动画,具体场景也可以根据自己需要替换为navigation的自定义转场动画实现
animateTo({
duration: 300, onFinish: () => {
this.pageInfos.pop();
}
}, () => {
this.opacityValue = 0;
})
return true;
})
.onReady((context: NavDestinationContext) => {
this.pageInfos = context.pathStack;
})
}
}
同样的上面实现了一个最简单案例,但是我们实际开发过程中会涉及到参数传递与持久化状态的问题,navigation的参数传递就简单多了,我们在使用NavPathStack.pushPath跳转的时候就传递参数即可,代码如下:
Button("使用navigation路由带参数")
.onClick(() => {
this.pageInfos.pushPath({ name: 'RouterOpacityPage2', param: 10 });
})
RouterOpacityPage2页面接受参数代码如下:
在NavDestination.onShow生命周期中获取路由栈里面的参数信息即可。
@Component
export struct RouterOpacityPage2 {
@State initialIndex: number = 0;
pageInfos: NavPathStack = new NavPathStack();
build() {
NavDestination() {
...
}
.onShown(() => {
if (this.pageInfos.getParamByName("RouterOpacityPage2")[0]) {
this.initialIndex = this.pageInfos.getParamByName("RouterOpacityPage2")[0] as number;
} else {
this.initialIndex = 0;
}
})
}
}
页面持久化方案是不销毁透明页面(RouterOpacityPage2)在路由栈中的信息,即返回首页(HomePage)的时候不要使用NavPathStack.pop方法让页面出栈,而是找到NavPathStack中首页(HomePage)的路由信息使用NavPathStack.push回到首页,这样透明页面(RouterOpacityPage2)在路由栈中的信息不会消失,我们在RouterOpacityPage2中的操作就可以持久化的保存下来,再次打开的时候就会回到我们上次关闭时的状态,相关实现代码如下:
Button("使用navigation带参数持久化")
.onClick(() => {
// 实现页面持久化需要使用navigation单例路由模式,当前暂无相关接口直接实现需要手动实现
let homeIndex = this.pageInfos.getIndexByName("RouterOpacityPage2");
if (homeIndex.length == 0) {
this.pageInfos.pushPath({ name: 'RouterOpacityPage2', param: 10 }, false);
return;
}
// 找到路由栈中RouterOpacityPage2的index使用moveIndexToTop接口移动到顶层让其显示
this.pageInfos.moveIndexToTop(homeIndex.pop(), false)
})
@Component
export struct RouterOpacityPage2 {
@State opacityValue: number = 1;
@State initialIndex: number = 0;
pageInfos: NavPathStack = new NavPathStack();
build() {
NavDestination() {
...
}
.onBackPressed(() => {
animateTo({
duration: 300, onFinish: () => {
if (this.initialIndex) {
// 实现页面持久化需要使用navigation单例路由模式,当前暂无相关接口直接实现需要手动实现
let homeIndex = this.pageInfos.getIndexByName("HomePage");
if (homeIndex.length == 0) {
this.pageInfos.pushPath({ name: "HomePage" }, false);
return;
}
this.pageInfos.moveIndexToTop(homeIndex.pop(), false);
} else {
this.pageInfos.pop();
}
}
}, () => {
this.opacityValue = 0;
})
return true;
})
}
}
注意:上面的方式使用时RouterOpacityPage2会一直存在路由栈中,为避免不必要的内存消耗可以根据需要在不需要持久保存的时候对路由栈进行pop出栈处理。
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=21742,转载请注明出处。
评论0