本文原创发布在华为开发者社区。
介绍
本示例介绍以下五种常见的弹窗场景化案例。
- 应用启动时的隐私政策和用户协议弹窗
- 网络哀求完成的结果提示弹窗
- 应用返回上一级页面的退出确认弹窗
- 个人信息填写的信息弹窗
- 应用使用过程中出现的付费类广告弹窗
弹窗场景化源码链接
效果预览
使用说明
- 进入应用会立马弹出一个隐私协议窗口,点同意关闭该窗口,点不同意退出应用。
- 点击网络哀求完成的结果提示弹窗,会弹出一个等待的子窗口弹窗,网络哀求完毕之后,会提示网络哀求的结果。
- 点击应用返回上一级页面的退出确认弹窗,手势滑动退出时会弹出一个确认退出的弹窗,只有点击确认了才会返回上一级页面。
- 点击个人信息填写的信息弹窗,点弹窗中的确认,会将弹窗中选择的数据返回给所需出现的页面。
- 点击应用使用过程中出现的付费类广告弹窗,会有一个自下而上的动画弹出广告弹窗,点击关闭时,同样会有一个自上而下的动画关闭弹窗。
实现思绪
应用启动时的隐私政策和用户协议弹窗
基于NavDestination组件的DIALOG模式实现此弹窗,点击《用户协议》或《隐私政策》跳转到对应网址,展现更加详细信息,同时使用用户所选项方式长期化保存到用户的选择,并通过onBackPressed拦截到退出操作,实现用户必须点击同意或者不同意才能使用App。焦点代码如下,源码参考PrivacyPolicyDialog.ets
- NavDestination() {
- Column() {
- Text() {
- Span('《用户协议》')
- .onClick(() => { // 点击跳转到具体链接
- this.pageInfos.pushPathByName('WebPage',
- 'https://id1.cloud.huawei.com/AMW/portal/agreements/userAgreement/zh-cn_userAgreement.html');
- })
- Span('《隐私政策》')
- .onClick(() => { // 点击跳转到具体链接
- this.pageInfos.pushPathByName('WebPage',
- 'https://id1.cloud.huawei.com/AMW/portal/agreements/accPrivacyStatement/zh-cn_accPrivacyStatement.html');
- })
- }
- Button('同意')
- .onClick(() => { // 点击持久化保存到用户的选择
- PreferenceMgr.getInstance().setValueSync<boolean>('isAgreePrivacyPolicy', true);
- this.pageInfos.pop();
- })
- Button('不同意')
- .onClick(() => { // 点击持久化保存到用户的选择
- PreferenceMgr.getInstance().setValueSync<boolean>('isAgreePrivacyPolicy', false);
- const context = getContext(this) as common.UIAbilityContext;
- context.terminateSelf();
- })
- }
- }
- .backgroundColor('#33000E03') // 设置一个具有透明度的背景,已达到蒙层的效果
- .mode(NavDestinationMode.DIALOG) // 设置mode为DIALOG
- .onBackPressed(() => {
- return true; // 返回true,则拦截到退出该页面的操作
- })
复制代码 网络哀求完成的结果提示弹窗
通过全局子窗口和全局自定义弹窗实现此弹窗。网络哀求过程中会先加载一个正在哀求样式的全局子窗口弹窗,哀求完毕之后,会加载哀求结果的一个全局自定义弹窗。焦点代码如下,源码参考HttpRequestDialog.ets,GlobalSubWindow.ets,GlobalCustomDialog.ets
- 全局子窗口
a) 获取应用的WindowStage,使用AppStorage方式保存
- onWindowStageCreate(windowStage: window.WindowStage): void {
- AppStorage.setOrCreate<window.WindowStage>('windowStage', windowStage); // 在应用生命周期内,保存WindowStage
- windowStage.loadContent('pages/DialogEntry', (err) => {
- if (err.code) {
- return;
- }
- });
- }
- export class GlobalSubWindow {
- // 全局子窗口中去获取上一步保存的WindowStage
- private windowStage: window.WindowStage | undefined = AppStorage.get<window.WindowStage>('windowStage');
- }
复制代码 b) 显示全局子窗口
- public showWindow(): void {
- if (this.subWindow) {
- return;
- }
- try {
- if (!this.windowStage) {
- return;
- }
- // 创建子窗口
- this.windowStage.createSubWindow('GlobalSubWindow', (err: BusinessError, data) => {
- if (err.code) {
- hilog.error(0x0000, 'GlobalSubWindowTag', 'create subWindow failed. %{public}s', JSON.stringify(err));
- return;
- }
- this.subWindow = data;
- if (this.subWindow) {
- this.subWindow.setWindowSystemBarEnable([]);
- this.subWindow.setWindowTouchable(true);
- this.loadContent(entryName); // 子窗口创建成功,去加载命名路由页面
- }
- })
- } catch (exception) {
- hilog.error(0x0000, 'GlobalSubWindowTag', 'create subWindow catch. %{public}s', JSON.stringify(exception));
- }
- }
复制代码 c) 关闭全局子窗口
- // 通过回调函数形式,是否需要在关闭后进行其他操作
- public closeWindow(callback?: CloseCallback): void {
- if (this.subWindow) {
- this.subWindow.destroyWindow((err: BusinessError) => {
- if (err.code) {
- hilog.error(0x0000, 'GlobalSubWindowTag', 'destroy subWindow failed. %{public}s', JSON.stringify(err));
- return;
- }
- this.subWindow = undefined;
- callback instanceof Function ? callback(err.message) : null;
- })
- } else {
- hilog.error(0x0000, 'GlobalSubWindowTag', 'close subWindow failed.');
- callback instanceof Function ? callback('close subWindow failed.') : null;
- }
- }
复制代码 - 全局自定义弹窗
- export class GlobalCustomDialog {
- private static instance: GlobalCustomDialog = new GlobalCustomDialog();
- private dialogId: number = 0;
- public static getInstance(): GlobalCustomDialog {
- return GlobalCustomDialog.instance;
- }
- public getDialogId() {
- return this.dialogId;
- }
- public open(content: string) {
- const that = GlobalContext.getInstance().getUIContext() as UIContext;
- promptAction.openCustomDialog({
- builder: CustomDialogBuilder.bind(that, content),
- alignment: DialogAlignment.Bottom,
- width: '45%',
- height: 36
- }).then((id: number) => {
- this.dialogId = id;
- })
- setTimeout(() => {
- result = '通过eventHub方法将结果返回';
- this.close();
- }, 1000);
- }
- public close() {
- const that = GlobalContext.getInstance().getUIContext() as UIContext;
- getContext(that).eventHub.emit('dialogReturnResult', result);
- promptAction.closeCustomDialog(this.dialogId);
- }
- }
复制代码 应用返回上一级页面的退出确认弹窗
通过onBackPressed拦截到退出操作,并将弹窗弹出。焦点代码如下,源码参考SideGestureInterceptDialog.ets
- NavDestination() {
- Column() {
- Text('Hello World!')
- .fontSize(50)
- }
- .height('100%')
- .justifyContent(FlexAlign.Center)
- }
- .alignSelf(ItemAlign.Center)
- .backgroundColor('#DDDDDD')
- .hideTitleBar(true)
- .onReady((context: NavDestinationContext) => {
- this.pageInfos = context.pathStack;
- })
- .onBackPressed((): boolean => {
- promptAction.showDialog(this.dialogOptions, (err, data) => {
- if (err) {
- hilog.info(0x0000, 'DialogDemoTag', '%{public}s', err);
- return;
- }
- if (data.index === 0) { // 根据弹框点击的按钮确认是否返回上一层页面
- this.pageInfos.pop();
- }
- })
- return true; // 返回true则拦截
- })
复制代码 个人信息填写的信息弹窗
通过@State和@Link关键字,实现将弹窗返回的数据通报给上一级页面。焦点代码如下,源码参考PersonInfoDialog.ets
- @CustomDialog
- export struct CustomDialogWidget {
- private controller?: CustomDialogController;
- private selectData: string = '';
- @Link result: string;
- @Require title: string = '';
- @Require dataSource: string[] = [];
- build() {
- Column({ space: 24 }) {
- ...
- Row({ space: 24 }) {
- Button('取消')
- .width('100%')
- .layoutWeight(1)
- .onClick(() => {
- this.controller?.close();
- })
- Button('确认')
- .width('100%')
- .layoutWeight(1)
- .onClick(() => {
- this.result = this.selectData;
- this.controller?.close();
- })
- }
- .width('100%')
- }
- ...
- }
- }
- @Component
- export struct PersonInfoDialog {
- @State birthDate: string = '';
- @State sex: string = '';
- @State hobbies: string = '';
- private hobbiesController: CustomDialogController = new CustomDialogController({
- builder: CustomDialogWidget({
- result: this.hobbies,
- title: '兴趣爱好',
- dataSource: ['足球', '羽毛球', '旅游', '打游戏', '看书']
- }),
- alignment: DialogAlignment.Bottom,
- customStyle: true,
- });
- build() {
- NavDestination() {
- Column({ space: 16 })
- ...
- this.TextCommonWidget({
- leftImage: $r('app.media.ic_hobbies'),
- leftText: '兴趣爱好(多选)',
- rightText: this.hobbies,
- rightImage: $r('app.media.ic_right_arrow'),
- onClick: () => {
- this.hobbiesController.open();
- }
- })
- }
- .margin(16)
- }
- .backgroundColor('#DDDDDD')
- .hideTitleBar(true)
- }
- }
复制代码 应用使用过程中出现的付费类广告弹窗
基于NavDestination组件的DIALOG模式实现此弹窗,弹窗弹出时出现自下而上的动画效果,关闭是出现自上而下的动画效果。焦点代码如下,源码参考AdvertDialog.ets
- @Styles
- clickEffectStyle() {
- .transition(TransitionEffect.OPACITY.animation({ duration: 300, curve: Curve.Friction }))
- .onClick(() => {
- animateTo({
- duration: 300,
- curve: Curve.Friction,
- onFinish: () => {
- this.pageInfos.pop();
- }
- }, () => {
- this.heightSize = '0%';
- })
- })
- }
- NavDestination() {
- Column({ space: 16 }) {
- Stack({ alignContent: Alignment.TopEnd }) {
- Image($r('app.media.advert_background'))
- .width('100%')
- .height(200)
- .borderRadius({ topLeft: 24, topRight: 24 })
- Image($r('app.media.ic_cancel'))
- .width(32)
- .height(32)
- .margin({ top: 24, right: 24 })
- .clickEffectStyle()
- }
- }
- .transition(
- TransitionEffect.move(TransitionEdge.BOTTOM)
- .animation({
- duration: 500,
- curve: Curve.Friction
- })
- )
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |