多种弹窗实现方法鸿蒙示例代码

打印 上一主题 下一主题

主题 961|帖子 961|积分 2883

本文原创发布在华为开发者社区。
介绍

本示例介绍以下五种常见的弹窗场景化案例。


  • 应用启动时的隐私政策和用户协议弹窗
  • 网络哀求完成的结果提示弹窗
  • 应用返回上一级页面的退出确认弹窗
  • 个人信息填写的信息弹窗
  • 应用使用过程中出现的付费类广告弹窗
弹窗场景化源码链接
效果预览


使用说明


  • 进入应用会立马弹出一个隐私协议窗口,点同意关闭该窗口,点不同意退出应用。
  • 点击网络哀求完成的结果提示弹窗,会弹出一个等待的子窗口弹窗,网络哀求完毕之后,会提示网络哀求的结果。
  • 点击应用返回上一级页面的退出确认弹窗,手势滑动退出时会弹出一个确认退出的弹窗,只有点击确认了才会返回上一级页面。
  • 点击个人信息填写的信息弹窗,点弹窗中的确认,会将弹窗中选择的数据返回给所需出现的页面。
  • 点击应用使用过程中出现的付费类广告弹窗,会有一个自下而上的动画弹出广告弹窗,点击关闭时,同样会有一个自上而下的动画关闭弹窗。
实现思绪

应用启动时的隐私政策和用户协议弹窗

基于NavDestination组件的DIALOG模式实现此弹窗,点击《用户协议》或《隐私政策》跳转到对应网址,展现更加详细信息,同时使用用户所选项方式长期化保存到用户的选择,并通过onBackPressed拦截到退出操作,实现用户必须点击同意或者不同意才能使用App。焦点代码如下,源码参考PrivacyPolicyDialog.ets
  1. NavDestination() {
  2.   Column() {
  3.     Text() {
  4.       Span('《用户协议》')
  5.         .onClick(() => {    // 点击跳转到具体链接
  6.           this.pageInfos.pushPathByName('WebPage',
  7.             'https://id1.cloud.huawei.com/AMW/portal/agreements/userAgreement/zh-cn_userAgreement.html');
  8.         })
  9.       Span('《隐私政策》')
  10.         .onClick(() => {    // 点击跳转到具体链接
  11.           this.pageInfos.pushPathByName('WebPage',
  12.             'https://id1.cloud.huawei.com/AMW/portal/agreements/accPrivacyStatement/zh-cn_accPrivacyStatement.html');
  13.         })
  14.     }
  15.     Button('同意')
  16.       .onClick(() => {      // 点击持久化保存到用户的选择
  17.         PreferenceMgr.getInstance().setValueSync<boolean>('isAgreePrivacyPolicy', true);
  18.         this.pageInfos.pop();
  19.       })
  20.     Button('不同意')
  21.       .onClick(() => {      // 点击持久化保存到用户的选择
  22.         PreferenceMgr.getInstance().setValueSync<boolean>('isAgreePrivacyPolicy', false);
  23.         const context = getContext(this) as common.UIAbilityContext;
  24.         context.terminateSelf();
  25.       })
  26.   }
  27. }
  28. .backgroundColor('#33000E03')       // 设置一个具有透明度的背景,已达到蒙层的效果
  29. .mode(NavDestinationMode.DIALOG)    // 设置mode为DIALOG
  30. .onBackPressed(() => {
  31.   return true;                      // 返回true,则拦截到退出该页面的操作
  32. })
复制代码
网络哀求完成的结果提示弹窗

通过全局子窗口和全局自定义弹窗实现此弹窗。网络哀求过程中会先加载一个正在哀求样式的全局子窗口弹窗,哀求完毕之后,会加载哀求结果的一个全局自定义弹窗。焦点代码如下,源码参考HttpRequestDialog.ets,GlobalSubWindow.ets,GlobalCustomDialog.ets

  • 全局子窗口
    a) 获取应用的WindowStage,使用AppStorage方式保存
    1. onWindowStageCreate(windowStage: window.WindowStage): void {
    2.     AppStorage.setOrCreate<window.WindowStage>('windowStage', windowStage);     // 在应用生命周期内,保存WindowStage
    3.     windowStage.loadContent('pages/DialogEntry', (err) => {
    4.       if (err.code) {
    5.         return;
    6.       }
    7.     });
    8.   }
    9. export class GlobalSubWindow {
    10.   // 全局子窗口中去获取上一步保存的WindowStage
    11.   private windowStage: window.WindowStage | undefined = AppStorage.get<window.WindowStage>('windowStage');
    12. }
    复制代码
    b) 显示全局子窗口
    1. public showWindow(): void {
    2.   if (this.subWindow) {
    3.     return;
    4.   }
    5.   try {
    6.     if (!this.windowStage) {
    7.       return;
    8.     }
    9.     // 创建子窗口
    10.     this.windowStage.createSubWindow('GlobalSubWindow', (err: BusinessError, data) => {
    11.       if (err.code) {
    12.         hilog.error(0x0000, 'GlobalSubWindowTag', 'create subWindow failed. %{public}s', JSON.stringify(err));
    13.         return;
    14.       }
    15.       this.subWindow = data;
    16.       if (this.subWindow) {
    17.         this.subWindow.setWindowSystemBarEnable([]);
    18.         this.subWindow.setWindowTouchable(true);
    19.         this.loadContent(entryName);    // 子窗口创建成功,去加载命名路由页面
    20.       }
    21.     })
    22.   } catch (exception) {
    23.     hilog.error(0x0000, 'GlobalSubWindowTag', 'create subWindow catch. %{public}s', JSON.stringify(exception));
    24.   }
    25. }
    复制代码
    c) 关闭全局子窗口
    1. // 通过回调函数形式,是否需要在关闭后进行其他操作
    2. public closeWindow(callback?: CloseCallback): void {
    3.   if (this.subWindow) {
    4.     this.subWindow.destroyWindow((err: BusinessError) => {
    5.       if (err.code) {
    6.         hilog.error(0x0000, 'GlobalSubWindowTag', 'destroy subWindow failed. %{public}s', JSON.stringify(err));
    7.         return;
    8.       }
    9.       this.subWindow = undefined;
    10.       callback instanceof Function ? callback(err.message) : null;
    11.     })
    12.   } else {
    13.     hilog.error(0x0000, 'GlobalSubWindowTag', 'close subWindow failed.');
    14.     callback instanceof Function ? callback('close subWindow failed.') : null;
    15.   }
    16. }
    复制代码
  • 全局自定义弹窗
  1. export class GlobalCustomDialog {
  2.   private static instance: GlobalCustomDialog = new GlobalCustomDialog();
  3.   private dialogId: number = 0;
  4.   public static getInstance(): GlobalCustomDialog {
  5.     return GlobalCustomDialog.instance;
  6.   }
  7.   public getDialogId() {
  8.     return this.dialogId;
  9.   }
  10.   public open(content: string) {
  11.     const that = GlobalContext.getInstance().getUIContext() as UIContext;
  12.     promptAction.openCustomDialog({
  13.       builder: CustomDialogBuilder.bind(that, content),
  14.       alignment: DialogAlignment.Bottom,
  15.       width: '45%',
  16.       height: 36
  17.     }).then((id: number) => {
  18.       this.dialogId = id;
  19.     })
  20.     setTimeout(() => {
  21.       result = '通过eventHub方法将结果返回';
  22.       this.close();
  23.     }, 1000);
  24.   }
  25.   public close() {
  26.     const that = GlobalContext.getInstance().getUIContext() as UIContext;
  27.     getContext(that).eventHub.emit('dialogReturnResult', result);
  28.     promptAction.closeCustomDialog(this.dialogId);
  29.   }
  30. }
复制代码
应用返回上一级页面的退出确认弹窗

通过onBackPressed拦截到退出操作,并将弹窗弹出。焦点代码如下,源码参考SideGestureInterceptDialog.ets
  1. NavDestination() {
  2.   Column() {
  3.     Text('Hello World!')
  4.       .fontSize(50)
  5.   }
  6.   .height('100%')
  7.   .justifyContent(FlexAlign.Center)
  8. }
  9. .alignSelf(ItemAlign.Center)
  10. .backgroundColor('#DDDDDD')
  11. .hideTitleBar(true)
  12. .onReady((context: NavDestinationContext) => {
  13.   this.pageInfos = context.pathStack;
  14. })
  15. .onBackPressed((): boolean => {
  16.   promptAction.showDialog(this.dialogOptions, (err, data) => {
  17.     if (err) {
  18.       hilog.info(0x0000, 'DialogDemoTag', '%{public}s', err);
  19.       return;
  20.     }
  21.     if (data.index === 0) { // 根据弹框点击的按钮确认是否返回上一层页面
  22.       this.pageInfos.pop();
  23.     }
  24.   })
  25.   return true; // 返回true则拦截
  26. })
复制代码
个人信息填写的信息弹窗

通过@State和@Link关键字,实现将弹窗返回的数据通报给上一级页面。焦点代码如下,源码参考PersonInfoDialog.ets
  1. @CustomDialog
  2. export struct CustomDialogWidget {
  3.   private controller?: CustomDialogController;
  4.   private selectData: string = '';
  5.   @Link result: string;
  6.   @Require title: string = '';
  7.   @Require dataSource: string[] = [];
  8.   build() {
  9.     Column({ space: 24 }) {
  10.       ...
  11.       Row({ space: 24 }) {
  12.         Button('取消')
  13.           .width('100%')
  14.           .layoutWeight(1)
  15.           .onClick(() => {
  16.             this.controller?.close();
  17.           })
  18.         Button('确认')
  19.           .width('100%')
  20.           .layoutWeight(1)
  21.           .onClick(() => {
  22.             this.result = this.selectData;
  23.             this.controller?.close();
  24.           })
  25.       }
  26.       .width('100%')
  27.     }
  28.     ...
  29.   }
  30. }
  31. @Component
  32. export struct PersonInfoDialog {
  33.   @State birthDate: string = '';
  34.   @State sex: string = '';
  35.   @State hobbies: string = '';
  36.   private hobbiesController: CustomDialogController = new CustomDialogController({
  37.     builder: CustomDialogWidget({
  38.       result: this.hobbies,
  39.       title: '兴趣爱好',
  40.       dataSource: ['足球', '羽毛球', '旅游', '打游戏', '看书']
  41.     }),
  42.     alignment: DialogAlignment.Bottom,
  43.     customStyle: true,
  44.   });
  45.   build() {
  46.     NavDestination() {
  47.       Column({ space: 16 })
  48.         ...
  49.         this.TextCommonWidget({
  50.           leftImage: $r('app.media.ic_hobbies'),
  51.           leftText: '兴趣爱好(多选)',
  52.           rightText: this.hobbies,
  53.           rightImage: $r('app.media.ic_right_arrow'),
  54.           onClick: () => {
  55.             this.hobbiesController.open();
  56.           }
  57.         })
  58.       }
  59.       .margin(16)
  60.     }
  61.     .backgroundColor('#DDDDDD')
  62.     .hideTitleBar(true)
  63.   }
  64. }
复制代码
应用使用过程中出现的付费类广告弹窗

基于NavDestination组件的DIALOG模式实现此弹窗,弹窗弹出时出现自下而上的动画效果,关闭是出现自上而下的动画效果。焦点代码如下,源码参考AdvertDialog.ets
  1. @Styles
  2. clickEffectStyle() {
  3.   .transition(TransitionEffect.OPACITY.animation({ duration: 300, curve: Curve.Friction }))
  4.   .onClick(() => {
  5.     animateTo({
  6.       duration: 300,
  7.       curve: Curve.Friction,
  8.       onFinish: () => {
  9.         this.pageInfos.pop();
  10.       }
  11.     }, () => {
  12.       this.heightSize = '0%';
  13.     })
  14.   })
  15. }
  16. NavDestination() {
  17.   Column({ space: 16 }) {
  18.     Stack({ alignContent: Alignment.TopEnd }) {
  19.       Image($r('app.media.advert_background'))
  20.         .width('100%')
  21.         .height(200)
  22.         .borderRadius({ topLeft: 24, topRight: 24 })
  23.       Image($r('app.media.ic_cancel'))
  24.         .width(32)
  25.         .height(32)
  26.         .margin({ top: 24, right: 24 })
  27.         .clickEffectStyle()
  28.     }
  29.   }
  30.   .transition(
  31.   TransitionEffect.move(TransitionEdge.BOTTOM)
  32.     .animation({
  33.       duration: 500,
  34.       curve: Curve.Friction
  35.     })
  36.   )
  37. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

渣渣兔

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表