DEV Community

HarmonyOS
HarmonyOS

Posted on

How to reuse the same subwindow in different locations on multiple pages

Read the original article:How to reuse the same subwindow in different locations on multiple pages

Requirement Description

To improve subwindow reusability, both the Index page and PageA page need to reuse the same subwindow.

  • On the Index page, the subwindow should appear above the TabBar.
  • On the PageA page, the subwindow should appear at the bottom of the page. The goal is to implement this behavior using a single reusable subwindow component.

Background Knowledge

In HarmonyOS, you can create a subwindow using the createSubWindow method.
When creating a subwindow, you can set its initial position using moveWindowTo, which defines the subwindow’s top-left corner coordinates.
After creation, you can find the subwindow by name using the window.findWindow method.
These APIs together allow dynamic control over subwindow positioning and visibility across multiple pages.

Implementation Steps

Step 1: Encapsulate the Subwindow in a Class

  • Create a utility class (e.g., Until.ets) to encapsulate subwindow logic.
  • This class provides methods to open, move, and close the subwindow.
  • Any page can import and call this class to reuse the same subwindow instance.

Step 2: Set Subwindow Properties

  • In the class, use createSubWindow to initialize the subwindow.
  • Set its UI content, background color, position, and size using setUIContent, setWindowBackgroundColor, moveWindowTo, and resize.
  • Finally, call showWindow() to display it.

Step 3: Control Subwindow Position from Different Pages

  • On the Index page, call OpenSubWindows() during the aboutToAppear() lifecycle to display the subwindow above the TabBar.
  • On the PageA page, use window.findWindow() to retrieve the existing subwindow and reposition it with moveWindowTo() so that it appears at the bottom.

Step 4: Handle Navigation and Cleanup

  • When navigating from the main page to the subpage, reposition the subwindow using moveWindowTo.
  • When navigating back, destroy and recreate the subwindow to reset its position.

Step 5: Customize Subwindow Content

  • The subwindow page (e.g., FloatPage.ets) defines the visible content of the floating window.
  • It can contain buttons for navigation and closing the subwindow.

Code Snippet / Configuration

1. Encapsulated Subwindow Class (Until.ets)

import { common } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';

export class Loading {
  win: window.Window | undefined = undefined;

  OpenSubWindows(UIContext: UIContext) {
    (UIContext?.getHostContext() as common.UIAbilityContext).windowStage.createSubWindow('FloatPage', (err, windowClass) => {
      if (err.code > 0) {
        hilog.error(0x0000, 'testTag', '%{public}s', `failed to create subWindow Cause: ${err.message}`);
        return;
      }
      this.win = windowClass;
      try {
        windowClass.setUIContent('pages/FloatPage', () => {
          windowClass.setWindowBackgroundColor('#00000000');
        });
        windowClass.moveWindowTo(0, 2285);
        windowClass.resize(1260, 200);
        windowClass.showWindow();
      } catch (err) {
        hilog.error(0x0000, 'testTag', '%{public}s', `failed to create subWindow Cause:${err}`);
      }
    })
  }

  CloseSubWindows() {
    this.win?.destroyWindow();
  }
}

export default new Loading()
Enter fullscreen mode Exit fullscreen mode

2. Subpage Logic (PageA.ets)

import { router, window } from '@kit.ArkUI';
import Loading from './Until';

@Entry
@Component
struct PageA {
  floatWindow: window.Window = window.findWindow('FloatPage')

  aboutToAppear(): void {
    this.floatWindow.moveWindowTo(0, 2500);
  }

  build() {
    Column()
      .width('100%')
      .height('100%')
      .backgroundColor('#5291FF')
      .onClick(() => {
        router.back()
        Loading.CloseSubWindows()
      })
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Main Page Logic (Index.ets)

import Loading from './Until';

@Entry
@Component
struct Index {
  @State fontColor: string = '#182431';
  @State selectedFontColor: string = '#007DFF';
  @State currentIndex: number = 0;
  @State selectedIndex: number = 0;
  private controller: TabsController = new TabsController();

  aboutToAppear(): void {
    Loading.OpenSubWindows(this.getUIContext())
  }

  build() {
    Column() {
      Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) {
        TabContent() {
          Column()
            .width('100%')
            .height('100%')
            .backgroundColor('#00CB87')
            .onClick(() => {
              Loading.OpenSubWindows(this.getUIContext())
            })
            .onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {
              if (isVisible && currentRatio >= 1.0) {
                Loading.OpenSubWindows(this.getUIContext())
              }
            })
        }.tabBar('Tab 0')

        TabContent() { Column().width('100%').height('100%').backgroundColor('#007DFF') }.tabBar('Tab 1')
        TabContent() { Column().width('100%').height('100%').backgroundColor('#FFBF00') }.tabBar('Tab 2')
        TabContent() { Column().width('100%').height('100%').backgroundColor('#E67C92') }.tabBar('Tab 3')
      }
      .vertical(false)
      .barMode(BarMode.Fixed)
      .barHeight(56)
      .animationDuration(400)
      .onChange((index: number) => { this.currentIndex = index; this.selectedIndex = index; })
      .onAnimationStart((index: number, targetIndex: number) => {
        if (index !== targetIndex) this.selectedIndex = targetIndex;
      })
      .height('100%')
      .backgroundColor('#F1F3F5')
    }.width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Subwindow Page (FloatPage.ets)

import { common } from '@kit.AbilityKit';
import Loading from './Until';

@Entry
@Component
struct FloatPage {
  @State message: string = 'Floating Window'
  private context = this.getUIContext().getHostContext() as common.UIAbilityContext;

  onBackPress() {
    Loading.CloseSubWindows()
    return true
  }

  build() {
    Row() {
      Text(this.message)
        .fontSize(15)
        .fontWeight(FontWeight.Bold)
        .margin({ left: '16vp' })

      Button('Go to New Page')
        .onClick(() => {
          const mainR = this.context.windowStage.getMainWindowSync()
            .getUIContext()
            .getRouter()
          mainR.pushUrl({ url: 'pages/PageA' })
        })

      Blank()

      Button('Close Subwindow')
        .onClick(() => { Loading.CloseSubWindows() })
        .margin({ right: '16vp' })
    }
    .width('100%')
    .height(50)
    .backgroundColor('#FFFFFF')
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

  • When on the Index page, the subwindow appears above the TabBar.
  • When navigating to PageA, the subwindow automatically repositions to the bottom.
  • When returning to the Index page, the subwindow is destroyed and recreated at the default position.

Limitations or Considerations

  • Requires API Version 19 Release or later.
  • Requires HarmonyOS 5.1.1 Release SDK or later.
  • Must be built and run using DevEco Studio 5.1.1 Release or later.

Related Documents or Links

https://developer.huawei.com/consumer/en/doc/harmonyos-references/arkts-apis-window-window#movewindowto9-1

https://developer.huawei.com/consumer/en/doc/harmonyos-references/arkts-apis-window-windowstage#createsubwindow9

Written by Emine Inan

Top comments (0)