文档管理中心
FAQ应用框架开发UI框架组件使用如何实现全局loading控件

如何实现全局loading控件

问题现象

实现一个可以作用于全局网络请求时,类似拦截器的loading弹窗,并在请求成功时关闭。

效果预览

背景知识

  • LoadingProgress:用于显示加载动效的组件。
  • window:当前窗口实例,窗口管理器管理的基本单元。

解决方案

构建一个新窗口用作全局loading控件,可在UI页面直接调用。使用window接口模拟实现网络请求拦截。定义新窗口,模拟弹窗,在窗口中自定义loading组件。并且实现沉浸式效果。

实现思路:HarmonyOS中自定义弹窗需要在@Component中才可以调用,而问题现象需要用在全局,window窗口可以实现此功能。在EntryAbility.ets文件中定义一个新窗口,封装window方法类用于后续调用,最后在UI页面中调用实现用作全局网络请求时拦截的loading弹窗。

  1. 在EntryAbility.ets定义窗口,并在onWindowStageCreate()函数中调用。代码如下:
    收起
    自动换行
    深色代码主题
    复制
    1. // 定义窗口
    2. subWindowStage: window.WindowStage | null = null;
    3. onWindowStageCreate(windowStage: window.WindowStage): void {
    4. // Main window is created, set main page for this ability
    5. hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    6. // onWindowStageCreate()函数,并且增加监听
    7. this.subWindowStage = windowStage;
    8. const that: EntryAbility = this;
    9. this.context.eventHub.on('createWindow', (data: Data) => {
    10. if (that.subWindowStage != undefined) {
    11. data.subWindowStage = that.subWindowStage;
    12. } else {
    13. hilog.info(0x0000, 'testTag', '%{public}s', 'that.subWindowStage == undefined');
    14. }
    15. });
    16. windowStage.loadContent('pages/Index', (err) => {
    17. if (err.code) {
    18. hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
    19. return;
    20. }
    21. hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    22. });
    23. }
  2. 封装window方法,CommonWindow.ets文件。
    收起
    自动换行
    深色代码主题
    复制
    1. import window from '@ohos.window';
    2. import common from '@ohos.app.ability.common';
    3. import { BusinessError } from '@ohos.base';
    4. import { entryName } from './MainPage';
    5. export class CommonWindow {
    6. private storage: LocalStorage | null = null;
    7. private subWindow: window.Window | null = null;
    8. private windowStageUtils: window.WindowStage | null = null;
    9. private init(ctx: common.UIAbilityContext) {
    10. let data: Data = { subWindowStage: null, storage: null };
    11. ctx.eventHub.emit('createWindow', data);
    12. this.windowStageUtils = data.subWindowStage;
    13. this.storage = data.storage;
    14. console.info('aboutToAppear end createWindowStage');
    15. ctx.eventHub.on('closeWindow', (data: Data) => {
    16. this.destroySubWindow();
    17. console.info(`data: ${JSON.stringify(data)}`);
    18. });
    19. }
    20. showWindow(ctx: common.UIAbilityContext) {
    21. this.init(ctx);
    22. if (this.subWindow) {
    23. console.info('subWindow is already exist');
    24. return;
    25. }
    26. try {
    27. if (!this.windowStageUtils) {
    28. console.error('this.windowStage1 is null');
    29. return;
    30. }
    31. this.windowStageUtils.createSubWindow('mySubWindow', (err: BusinessError, data) => {
    32. const errCode: number = err.code;
    33. if (errCode) {
    34. console.error(`Failed to create the subWindow. Cause: ${JSON.stringify(err)}`);
    35. return;
    36. }
    37. this.subWindow = (data as window.Window);
    38. console.info(`Succeeded in creating the subWindow. Data: ${JSON.stringify(data)}`);
    39. if (!this.subWindow) {
    40. console.info('Failed to load the content. Cause: windowClass is null');
    41. } else {
    42. let names: Array<'status' | 'navigation'> = [];
    43. this.subWindow.setWindowSystemBarEnable(names);
    44. this.subWindow.setWindowTouchable(false); // 设置是否可以点击
    45. this.loadContent(entryName);
    46. this.showSubWindow();
    47. }
    48. });
    49. } catch (exception) {
    50. console.error(`Failed to create the window. Cause: ${JSON.stringify(exception)}`);
    51. }
    52. }
    53. private showSubWindow() {
    54. if (this.subWindow) {
    55. this.subWindow.showWindow((err: BusinessError) => {
    56. const errCode: number = err.code;
    57. if (errCode) {
    58. console.error(`Failed to show the window. Cause: ${JSON.stringify(err)} `);
    59. return;
    60. }
    61. console.info('Succeeded in showing the window.');
    62. });
    63. } else {
    64. console.info('showSubWindow subWindow not created.');
    65. }
    66. }
    67. destroySubWindow() {
    68. if (this.subWindow) {
    69. this.subWindow.destroyWindow((err) => {
    70. const errCode: number = err.code;
    71. if (errCode) {
    72. console.error(`Failed to destroy the window. Cause: ${JSON.stringify(err)}`);
    73. return;
    74. }
    75. this.subWindow = null;
    76. });
    77. } else {
    78. console.info('showSubWindow subWindow not created.');
    79. }
    80. }
    81. private loadContent(path: string) {
    82. if (this.subWindow) {
    83. let that = this;
    84. let pAra: Record<string, number> = { 'PropA': 66 };
    85. that.storage = new LocalStorage(pAra);
    86. if (that.storage != null && this.subWindow != null) {
    87. that.storage.setOrCreate('windowObj', this.subWindow);
    88. }
    89. this.subWindow.loadContentByName(path, this.storage, (err: BusinessError) => {
    90. const errCode: number = err.code;
    91. if (errCode) {
    92. return;
    93. }
    94. if (this.subWindow) {
    95. this.subWindow.setWindowBackgroundColor('#88000000');
    96. }
    97. });
    98. } else {
    99. console.info('loadContent subWindow not created.');
    100. }
    101. }
    102. }
    103. export interface Data {
    104. subWindowStage: window.WindowStage | null,
    105. storage: LocalStorage | null
    106. }
  3. MainPage页面,沉浸式弹窗页面。
    收起
    自动换行
    深色代码主题
    复制
    1. import window from '@ohos.window';
    2. export const entryName: string = 'loadingPage';
    3. @Entry({ routeName: entryName })
    4. @Component
    5. export struct MainPage {
    6. @LocalStorageLink('PropA') varA: number | undefined = 1;
    7. localStorage = this.getUIContext().getSharedLocalStorage();
    8. // 页面生命周期:打开沉浸式
    9. onPageShow() {
    10. window.getLastWindow(this.getUIContext().getHostContext(), (err, win) => {
    11. // 获取当前窗口的属性
    12. let prop: window.WindowProperties = win.getWindowProperties();
    13. // 打印当前窗口属性
    14. console.info(JSON.stringify(prop));
    15. console.error(`err: ${err}`);
    16. win.setWindowLayoutFullScreen(true);
    17. });
    18. }
    19. // 页面生命周期:关闭沉浸式
    20. onPageHide() {
    21. window.getLastWindow(this.getUIContext().getHostContext(), (err, win) => {
    22. console.error(`err: ${err}`);
    23. win.setWindowLayoutFullScreen(false);
    24. });
    25. }
    26. aboutToAppear() {
    27. this.varA = this.localStorage?.get<number>('PropA');
    28. }
    29. build() {
    30. Column() {
    31. LoadingProgress()
    32. .width(72)
    33. .color('#88ffffff')
    34. }
    35. .justifyContent(FlexAlign.Center)
    36. .height('100%')
    37. .width('100%');
    38. }
    39. }
  4. UI页面,初始页,调用window类,按钮唤出弹窗。
    收起
    自动换行
    深色代码主题
    复制
    1. import { CommonWindow } from '../utils/CommonWindow';
    2. import { common } from '@kit.AbilityKit';
    3. @Entry
    4. @Component
    5. struct Index {
    6. ctx: common.UIAbilityContext | undefined = undefined;
    7. aboutToAppear(): void {
    8. this.ctx = this.getUIContext().getHostContext() as common.UIAbilityContext;
    9. }
    10. testSubWindowDialog() {
    11. let window = new CommonWindow();
    12. if (!this.ctx) {
    13. return;
    14. }
    15. window.showWindow(this.ctx);
    16. setTimeout(() => {
    17. window.destroySubWindow();
    18. }, 2000);
    19. }
    20. build() {
    21. Row() {
    22. Column() {
    23. Button('子窗口弹窗')
    24. .margin({ top: 20 })
    25. .onClick(() => {
    26. this.testSubWindowDialog();
    27. });
    28. }
    29. .width('100%');
    30. }
    31. .height('100%');
    32. }
    33. }

总结

运用窗口特性,封装类似弹窗的效果,相比于常规弹窗CustomDialoggetPromptAction().openCustomDialog不局限于依赖UI页面,在使用时自定义UI样式,可直接调用。

在 FAQ 中进行搜索
请输入您想要搜索的关键词