文档管理中心

平板弹窗被遮挡

问题现象

在横屏模式下打开应用,点击“已有账号登录”后,登录弹窗从底部弹出,未居中显示,且无法点击弹窗中的内容。切换为竖屏模式后,同样点击“已有账号登录”,弹窗底部内容被截断,信息显示不完整。

背景知识

关于弹窗,根据用户交互操作场景,可分为模态弹窗和非模态弹窗两种类型,其区别在于用户是否必须对其做出响应。

  • 模态弹窗为强交互形式,会中断用户当前的操作流程,要求用户必须做出响应才能继续其他操作,通常用于需要向用户传达重要信息的场景;
  • 非模态弹窗,为弱交互形式,不会影响用户当前操作行为,用户可以不对其进行回应,通常都有时间限制,出现一段时间后会自动消失。一般用于告诉用户信息内容外还需要用户进行功能操作的场景。

问题定位

  • 排查自定义弹窗中@Builder自定义构建函数修饰的自定义组件中是否设置的height属性,强制固定了弹窗的高度,导致自定义弹窗的内容被截断,显示不全。
  • 排查自定义弹窗中BaseDialogOptions属性中是否传入并设置了offset属性强行给自定义弹窗设置了向下的偏移量,导致弹窗向下偏移被遮挡。

分析结论

  • 自定义弹窗的自定义构建函数@Builder修饰的自定义组件中设置了height属性,并且值被写死,导致自定义弹窗内容超出部分被截断。
  • 自定义弹窗的选项属性BaseDialogOptions中传入了offset属性值,在Y轴方向上设置了偏移量,导致弹窗贴底被遮挡。

修改建议

由于CustomDialog在使用上存在较多限制,不支持动态创建和动态刷新,在相对较复杂的应用场景中推荐使用UIContext中获取到的PromptAction对象提供的openCustomDialog接口来实现自定义弹出框。以下是基于PromptAction封装的自定义弹窗实现横竖屏居中显示不遮挡的示例,具体实现步骤如下:

  • 创建ComponentContent。ComponentContent用于定义自定义弹出框的内容。其中,wrapBuilder(buildText)封装自定义组件,new Params(this.message)是自定义组件的入参,可以缺省,也可以传入基础数据类型。
    收起
    自动换行
    深色代码主题
    复制
    1. private contentNode: ComponentContent<Object> =
    2. new ComponentContent(this.ctx, wrapBuilder(buildText));
  • 打开自定义弹出框。通过调用openCustomDialog接口打开的弹出框默认customStyle为true,即弹出框的内容样式完全按照contentNode自定义样式显示。
    收起
    自动换行
    深色代码主题
    复制
    1. PromptActionClass.ctx.getPromptAction()
    2. .openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
    3. .then(() => {
    4. console.info('OpenCustomDialog complete.');
    5. })
    6. .catch((error: BusinessError) => {
    7. let message = (error as BusinessError).message;
    8. let code = (error as BusinessError).code;
    9. console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
    10. });
  • 关闭自定义弹出框。由于closeCustomDialog接口需要传入待关闭弹出框对应的ComponentContent。因此,如果需要在弹出框中设置关闭方法,则可参考完整示例封装静态方法来实现。关闭弹出框之后若需要释放对应的ComponentContent,则需要调用ComponentContent的dispose方法。
    收起
    自动换行
    深色代码主题
    复制
    1. PromptActionClass.ctx.getPromptAction()
    2. .closeCustomDialog(PromptActionClass.contentNode)
    3. .then(() => {
    4. console.info('CloseCustomDialog complete.');
    5. })
    6. .catch((error: BusinessError) => {
    7. let message = (error as BusinessError).message;
    8. let code = (error as BusinessError).code;
    9. console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
    10. });
  • PromptAction的BaseDialogOptions选项中offset属性值在Y轴方向上设置偏移量为0。
    收起
    自动换行
    深色代码主题
    复制
    1. aboutToAppear(): void {
    2. PromptActionClass.setContext(this.ctx);
    3. PromptActionClass.setContentNode(this.contentNode);
    4. PromptActionClass.setOptions({ alignment: DialogAlignment.Center });
    5. }
  • 自定义组件的height属性不设置或设置为auto,高度由内容撑开。
    收起
    自动换行
    深色代码主题
    复制
    1. @Builder
    2. function buildText() {
    3. Column() {
    4. Flex({ justifyContent: FlexAlign.SpaceBetween }) {
    5. Text('账号登录').fontSize(24).fontWeight(FontWeight.Bold);
    6. Image($r('app.media.close'))
    7. .width(32)
    8. .height(32)
    9. .onClick(() => {
    10. PromptActionClass.closeDialog();
    11. });
    12. }.padding({ bottom: 24 });
    13. Column() {
    14. TextInput({ placeholder: '邮箱' })
    15. .width('100%')
    16. .showUnderline(true);
    17. TextInput({ placeholder: '验证码' })
    18. .width('100%')
    19. .showUnderline(true);
    20. Row() {
    21. Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
    22. .select(false)
    23. .selectedColor(0x39a2db)
    24. .shape(CheckBoxShape.ROUNDED_SQUARE)
    25. .onChange((value: boolean) => {
    26. console.info('Checkbox2 change is' + value);
    27. });
    28. Text('我已阅读并同意《隐私政策》');
    29. }.padding({ top: 12, bottom: 12 });
    30. };
    31. Button('登录')
    32. .width('100%')
    33. .onClick(() => {
    34. PromptActionClass.closeDialog();
    35. });
    36. Column() {
    37. Text('其他账号登录').padding({ bottom: 12 });
    38. Row() {
    39. Image($r('app.media.huawei')).width(32).height(32).margin({ right: 12 });
    40. };
    41. }
    42. .padding({ top: 24 });
    43. }.backgroundColor('#FFF0F0F0')
    44. .padding(24)
    45. .width(480)
    46. .border({ radius: '10%' });
    47. }

完整示例如下:

收起
自动换行
深色代码主题
复制
  1. import { BusinessError } from '@kit.BasicServicesKit';
  2. import { ComponentContent, promptAction, UIContext } from '@kit.ArkUI';
  3. export class PromptActionClass {
  4. static ctx: UIContext;
  5. static contentNode: ComponentContent<Object>;
  6. static options: promptAction.BaseDialogOptions;
  7. static setContext(context: UIContext) {
  8. PromptActionClass.ctx = context;
  9. }
  10. static setContentNode(node: ComponentContent<Object>) {
  11. PromptActionClass.contentNode = node;
  12. }
  13. static setOptions(options: promptAction.BaseDialogOptions) {
  14. PromptActionClass.options = options;
  15. }
  16. static openDialog() {
  17. if (PromptActionClass.contentNode !== null) {
  18. PromptActionClass.ctx.getPromptAction()
  19. .openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
  20. .then(() => {
  21. console.info('OpenCustomDialog complete.');
  22. })
  23. .catch((error: BusinessError) => {
  24. let message = (error as BusinessError).message;
  25. let code = (error as BusinessError).code;
  26. console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
  27. });
  28. }
  29. }
  30. static closeDialog() {
  31. if (PromptActionClass.contentNode !== null) {
  32. PromptActionClass.ctx.getPromptAction()
  33. .closeCustomDialog(PromptActionClass.contentNode)
  34. .then(() => {
  35. console.info('CloseCustomDialog complete.');
  36. })
  37. .catch((error: BusinessError) => {
  38. let message = (error as BusinessError).message;
  39. let code = (error as BusinessError).code;
  40. console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
  41. });
  42. }
  43. }
  44. static updateDialog(options: promptAction.BaseDialogOptions) {
  45. if (PromptActionClass.contentNode !== null) {
  46. PromptActionClass.ctx.getPromptAction()
  47. .updateCustomDialog(PromptActionClass.contentNode, options)
  48. .then(() => {
  49. console.info('UpdateCustomDialog complete.');
  50. })
  51. .catch((error: BusinessError) => {
  52. let message = (error as BusinessError).message;
  53. let code = (error as BusinessError).code;
  54. console.error(`UpdateCustomDialog args error code is ${code}, message is ${message}`);
  55. });
  56. }
  57. }
  58. }
收起
自动换行
深色代码主题
复制
  1. // Index.ets
  2. import { ComponentContent } from '@kit.ArkUI';
  3. import { PromptActionClass } from './PromptActionView';
  4. @Builder
  5. function buildText() {
  6. Column() {
  7. Flex({ justifyContent: FlexAlign.SpaceBetween }) {
  8. Text('账号登录').fontSize(24).fontWeight(FontWeight.Bold);
  9. Image($r('app.media.close'))
  10. .width(32)
  11. .height(32)
  12. .onClick(() => {
  13. PromptActionClass.closeDialog();
  14. });
  15. }.padding({ bottom: 24 });
  16. Column() {
  17. TextInput({ placeholder: '邮箱' })
  18. .width('100%')
  19. .showUnderline(true);
  20. TextInput({ placeholder: '验证码' })
  21. .width('100%')
  22. .showUnderline(true);
  23. Row() {
  24. Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
  25. .select(false)
  26. .selectedColor(0x39a2db)
  27. .shape(CheckBoxShape.ROUNDED_SQUARE)
  28. .onChange((value: boolean) => {
  29. console.info('Checkbox2 change is' + value);
  30. });
  31. Text('我已阅读并同意《隐私政策》');
  32. }.padding({ top: 12, bottom: 12 });
  33. };
  34. Button('登录')
  35. .width('100%')
  36. .onClick(() => {
  37. PromptActionClass.closeDialog();
  38. });
  39. Column() {
  40. Text('其他账号登录').padding({ bottom: 12 });
  41. Row() {
  42. Image($r('app.media.huawei')).width(32).height(32).margin({ right: 12 });
  43. };
  44. }
  45. .padding({ top: 24 });
  46. }.backgroundColor('#FFF0F0F0')
  47. .padding(24)
  48. .width(480)
  49. .border({ radius: '10%' });
  50. }
  51. @Entry
  52. @Component
  53. struct Index {
  54. private ctx: UIContext = this.getUIContext();
  55. private contentNode: ComponentContent<Object> =
  56. new ComponentContent(this.ctx, wrapBuilder(buildText));
  57. aboutToAppear(): void {
  58. PromptActionClass.setContext(this.ctx);
  59. PromptActionClass.setContentNode(this.contentNode);
  60. PromptActionClass.setOptions({ alignment: DialogAlignment.Center });
  61. }
  62. build() {
  63. Column() {
  64. Button('已有账号登录')
  65. .margin({ bottom: 24 })
  66. .onClick(() => {
  67. PromptActionClass.openDialog();
  68. });
  69. }
  70. .justifyContent(FlexAlign.End)
  71. .width('100%')
  72. .height('100%');
  73. }
  74. }
在 FAQ 中进行搜索
请输入您想要搜索的关键词