HarmonyOS开发探索:ArkUI的透明页面效果

185 篇文章 0 订阅
181 篇文章 1 订阅

场景描述

透明页面也可以叫做弹窗页面,实际开发场景中经常有一个页面覆盖在另一个页面上的效果,例如:评论弹窗页面、广告弹窗页面等。

场景:评论弹窗页面

功能点:

  • 弹窗页面拉起。

  • 评论页面状态持久化保存。

  • 带参页面拉起。

方案一:使用router+subWindow实现

router路由无法更改页面模式,所以无法直接实现透明页面,需要借助拉起子窗口的方案实现透明页面的效果。思路如下:

  1. 获取窗口实例。

  2. 拉起一个子窗口并加载对应页面。

  3. 设置子窗口背景透明。

  4. 定义子窗口的关闭方案。

核心代码

在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()

})

}

}
 

实现效果如下所示:

cke_32140.gif

页面参数传递方案

因为窗口之前没有提供数据传递的API,所以无法直接传递页面参数;但是每个窗口都有自己的UIContext,可以通过UIContext获取其他窗口的router路由栈,并进行参数传递操作,但是因为该方案会造成不必要是内存消耗,影响性能,并且实现起来较复杂这里只提供思路,不做具体实现,相关功能在navigation路由中实现。

方案二:使用DIALOG类型NavDestination实现【推荐】

使用navigation作为路由框架时,实现透明页面只需要设置页面的NavDestinationMode属性为DIALOG模式思路如下:

  1. 使用navigation作为页面跟容器。

  2. 跳转NavDestination页面并设置其mode属性为NavDestinationMode.DIALOG。

  3. 添加自定义转场动画。这里使用的组件转场,可根据实际需要替换为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出栈处理。

实现效果如下:

cke_15118.gif

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

点击领取→【纯血版鸿蒙全套最新学习资料】(安全链接,放心点击希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。


 鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

HarmonyOS Next 最新全套视频教程

 《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

大厂面试必问面试题

鸿蒙南向开发技术

鸿蒙APP开发必备

鸿蒙生态应用开发白皮书V2.0PDF


请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

                   

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值