【中秋國慶不斷更】HarmonyOS應用視窗管理(Stage模型)

HarmonyOS開發者社群發表於2023-10-02

一、視窗開發概述

視窗模組的定義

視窗模組用於在同一塊物理螢幕上,提供多個應用介面顯示、互動的機制。

●  對應用開發者而言,視窗模組提供了介面顯示和互動能力。

●  對終端使用者而言,視窗模組提供了控制應用介面的方式。

●  對整個作業系統而言,視窗模組提供了不同應用介面的組織管理邏輯。

視窗模組的用途

在HarmonyOS中,視窗模組主要負責以下職責:

●  提供應用和系統介面的視窗物件。 應用開發者透過視窗載入UI介面,實現介面顯示功能。

●  組織不同視窗的顯示關係,即維護不同視窗間的疊加層次和位置屬性。 應用和系統的視窗具有多種型別,不同型別的視窗具有不同的預設位置和疊加層次(Z軸高度)。同時,使用者操作也可以在一定範圍內對視窗的位置和疊加層次進行調整。

●  提供視窗動效。 在視窗顯示、隱藏及視窗間切換時,視窗模組通常會新增動畫效果,以使各個互動過程更加連貫流暢。在HarmonyOS中,應用視窗的動效為預設行為,不需要開發者進行設定或者修改。

●  指導輸入事件分發。 即根據當前視窗的狀態或焦點,進行事件的分發。觸控和滑鼠事件根據視窗的位置和尺寸進行分發,而鍵盤事件會被分發至焦點視窗。應用開發者可以透過視窗模組提供的介面設定視窗是否可以觸控和是否可以獲焦。

基本概念

HarmonyOS的視窗模組將視窗介面分為系統視窗、應用視窗兩種基本型別。

●  系統視窗:系統視窗指完成系統特定功能的視窗。如音量條、桌布、通知欄、狀態列、導航欄等。

●  應用視窗:應用視窗區別於系統視窗,指與應用顯示相關的視窗。根據顯示內容的不同,應用視窗又分為應用主視窗、應用子視窗兩種型別。應用主視窗:應用主視窗用於顯示應用介面,會在"任務管理介面"顯示。

○  應用子視窗:應用子視窗用於顯示應用的彈窗、懸浮窗等輔助視窗,不會在"任務管理介面"顯示。應用子視窗的生命週期跟隨應用主視窗。

實現原理

當前視窗的實現和開發與應用開發模型相關聯,不同模型下的介面功能略有區別。當前應用開發模型分為FA模型和Stage模型。

兩個模型的整體架構和設計思想,詳見 應用模型解讀

針對視窗開發,推薦使用Stage模型進行相關開發。

約束與限制

●  應用主視窗與子視窗存在大小限制,寬度範圍:[320, 2560],高度範圍:[240, 2560],單位為vp。

二、管理應用視窗(Stage模型)

基本概念

●  視窗沉浸式能力:指對狀態列、導航欄等系統視窗進行控制,減少狀態列導航欄等系統介面的突兀感,從而使使用者獲得最-佳體驗的能力。沉浸式能力只在應用主視窗作為全屏視窗時生效。通常情況下,應用子視窗(彈窗、懸浮視窗等輔助視窗)無法使用沉浸式能力。

●  懸浮窗:全域性懸浮視窗是一種特殊的應用視窗,具備在應用主視窗和對應Ability退至後臺後仍然可以在前臺顯示的能力。

懸浮視窗可以用於應用退至後臺後,使用小窗繼續播放影片,或者為特定的應用建立懸浮球等快速入口。應用在建立懸浮視窗前,需要申請對應的許可權。

場景介紹

在Stage模型下,管理應用視窗的典型場景有:

●  設定應用主視窗屬性及目標頁面

●  設定應用子視窗屬性及目標頁面

●  體驗視窗沉浸式能力

●  設定懸浮窗

以下分別介紹具體開發方式。

介面說明

上述場景涉及的常用介面如下表所示。更多API說明請參見 API參考

例項名

介面名

描述

WindowStage

getMainWindow(callback: AsyncCallback<Window>): void

獲取WindowStage例項下的主視窗。

此介面僅可在Stage模型下使用。

WindowStage

loadContent(path: string, callback: AsyncCallback<void>): void

為當前WindowStage的主視窗載入具體頁面。

此介面僅可在Stage模型下使用。

WindowStage

createSubWindow(name: string, callback: AsyncCallback<Window>): void

建立子視窗。

此介面僅可在Stage模型下使用。

window靜態方法

createWindow(config: Configuration, callback: AsyncCallback<Window>): void

建立系統視窗。

-config:建立視窗時的引數。

Window

setUIContent(path: string, callback: AsyncCallback<void>): void

為當前視窗載入具體頁面。

Window

setWindowBackgroundColor(color: string, callback: AsyncCallback<void>): void

設定視窗的背景色。

Window

setWindowBrightness(brightness: number, callback: AsyncCallback<void>): void

設定螢幕亮度值。

Window

setWindowTouchable(isTouchable: boolean, callback: AsyncCallback<void>): void

設定視窗是否為可觸狀態。

Window

moveWindowTo(x: number, y: number, callback: AsyncCallback<void>): void

移動當前視窗位置。

Window

resize(width: number, height: number, callback: AsyncCallback<void>): void

改變當前視窗大小。

Window

setWindowSystemBarEnable(names: Array<'status'|'navigation'>): Promise<void>

設定導航欄、狀態列是否顯示。

Window

showWindow(callback: AsyncCallback<void>): void

顯示當前視窗。

Window

on(type: 'touchOutside', callback: Callback<void>): void

開啟本視窗區域外的點選事件的監聽。

Window

destroyWindow(callback: AsyncCallback<void>): void

銷燬當前視窗。

設定應用主視窗

在Stage模型下,應用主視窗由UIAbility建立並維護生命週期。在UIAbility的onWindowStageCreate回撥中,透過WindowStage獲取應用主視窗,即可對其進行屬性設定等操作。還可以在應用配置檔案中設定應用主視窗的屬性,如最大視窗寬度maxWindowWidth等,詳見 module.json5配置檔案

開發步驟

1.      獲取應用主視窗。透過getMainWindow介面獲取應用主視窗。

2.      設定主視窗屬性。可設定主視窗的背景色、亮度值、是否可觸等多個屬性,開發者可根據需要選擇對應的介面。本示例以設定“是否可觸”屬性為例。

3.      為主視窗載入對應的目標頁面。透過loadContent介面載入主視窗的目標頁面。

import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
    onWindowStageCreate(windowStage) {
        // 1.獲取應用主視窗。
        let windowClass = null;
        windowStage.getMainWindow((err, data) => {
            if (err.code) {
                console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err));
                return;
            }
            windowClass = data;
            console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data));
            // 2.設定主視窗屬性。以設定"是否可觸"屬性為例。
            let isTouchable = true;
            windowClass.setWindowTouchable(isTouchable, (err) => {
                if (err.code) {
                    console.error('Failed to set the window to be touchable. Cause:' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in setting the window to be touchable.');
            })
        })
        // 3.為主視窗載入對應的目標頁面。
        windowStage.loadContent("pages/page2", (err) => {
            if (err.code) {
                console.error('Failed to load the content. Cause:' + JSON.stringify(err));
                return;
            }
            console.info('Succeeded in loading the content.');
        });
    }
};

設定應用子視窗

開發者可以按需建立應用子視窗,如彈窗等,並對其進行屬性設定等操作。

開發步驟

1.      建立應用子視窗。透過createSubWindow介面建立應用子視窗。

2.      設定子視窗屬性。子視窗建立成功後,可以改變其大小、位置等,還可以根據應用需要設定視窗背景色、亮度等屬性。

3.      載入顯示子視窗的具體內容。透過setUIContent和showWindow介面載入顯示子視窗的具體內容。

4.      銷燬子視窗。當不再需要某些子視窗時,可根據具體實現邏輯,使用destroyWindow介面銷燬子視窗。

import UIAbility from '@ohos.app.ability.UIAbility';
let windowStage_ = null;
let sub_windowClass = null;
export default class EntryAbility extends UIAbility {
    showSubWindow() {
        // 1.建立應用子視窗。
        windowStage_.createSubWindow("mySubWindow", (err, data) => {
            if (err.code) {
                console.error('Failed to create the subwindow. Cause: ' + JSON.stringify(err));
                return;
            }
            sub_windowClass = data;
            console.info('Succeeded in creating the subwindow. Data: ' + JSON.stringify(data));
            // 2.子視窗建立成功後,設定子視窗的位置、大小及相關屬性等。
            sub_windowClass.moveWindowTo(300, 300, (err) => {
                if (err.code) {
                    console.error('Failed to move the window. Cause:' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in moving the window.');
            });
            sub_windowClass.resize(500, 500, (err) => {
                if (err.code) {
                    console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in changing the window size.');
            });
            // 3.為子視窗載入對應的目標頁面。
            sub_windowClass.setUIContent("pages/page3", (err) => {
                if (err.code) {
                    console.error('Failed to load the content. Cause:' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in loading the content.');
                // 3.顯示子視窗。
                sub_windowClass.showWindow((err) => {
                    if (err.code) {
                        console.error('Failed to show the window. Cause: ' + JSON.stringify(err));
                        return;
                    }
                    console.info('Succeeded in showing the window.');
                });
            });
        })
    }
    destroySubWindow() {
        // 4.銷燬子視窗。當不再需要子視窗時,可根據具體實現邏輯,使用destroy對其進行銷燬。
        sub_windowClass.destroyWindow((err) => {
            if (err.code) {
                console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err));
                return;
            }
            console.info('Succeeded in destroying the window.');
        });
    }
    onWindowStageCreate(windowStage) {
        windowStage_ = windowStage;
        // 開發者可以在適當的時機,如主視窗上按鈕點選事件等,建立子視窗。並不一定需要在onWindowStageCreate呼叫,這裡僅作展示
        this.showSubWindow();
    }
    onWindowStageDestroy() {
        // 開發者可以在適當的時機,如子視窗上點選關閉按鈕等,銷燬子視窗。並不一定需要在onWindowStageDestroy呼叫,這裡僅作展示
        this.destroySubWindow();
    }
};

體驗視窗沉浸式能力

在看影片、玩遊戲等場景下,使用者往往希望隱藏狀態列、導航欄等不必要的系統視窗,從而獲得更佳的沉浸式體驗。此時可以藉助視窗沉浸式能力(視窗沉浸式能力都是針對應用主視窗而言的),達到預期效果。

開發步驟

1.      獲取應用主視窗。透過getMainWindow介面獲取應用主視窗。

2.      實現沉浸式效果。呼叫setWindowSystemBarEnable介面,設定導航欄、狀態列不顯示,從而達到沉浸式效果。

3.      載入顯示沉浸式視窗的具體內容。透過loadContent介面載入沉浸式視窗的具體內容。

import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
    onWindowStageCreate(windowStage) {
        // 1.獲取應用主視窗。
        let windowClass = null;
        windowStage.getMainWindow((err, data) => {
            if (err.code) {
                console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err));
                return;
            }
            windowClass = data;
            console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data));
            // 2.實現沉浸式效果:設定導航欄、狀態列不顯示。
            let names = [];
            windowClass.setWindowSystemBarEnable(names, (err) => {
                if (err.code) {
                    console.error('Failed to set the system bar to be visible. Cause:' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in setting the system bar to be visible.');
            });
        })
        // 3.為沉浸式視窗載入對應的目標頁面。
        windowStage.loadContent("pages/page2", (err) => {
            if (err.code) {
                console.error('Failed to load the content. Cause:' + JSON.stringify(err));
                return;
            }
            console.info('Succeeded in loading the content.');
        });
    }
};

設定懸浮窗

懸浮窗可以在已有的任務基礎上,建立一個始終在前臺顯示的視窗。即使建立懸浮窗的任務退至後臺,懸浮窗仍然可以在前臺顯示。通常懸浮窗位於所有應用視窗之上;開發者可以建立懸浮窗,並對懸浮窗進行屬性設定等操作。

開發步驟

前提條件:建立WindowType.TYPE_FLOAT即懸浮窗型別的視窗,需要申請ohos.permission.SYSTEM_FLOAT_WINDOW許可權,配置方式請參見 配置檔案許可權宣告

1.      建立懸浮窗。透過window.createWindow介面建立懸浮窗型別的視窗。

2.      對懸浮窗進行屬性設定等操作。懸浮窗視窗建立成功後,可以改變其大小、位置等,還可以根據應用需要設定懸浮窗背景色、亮度等屬性。

3.      載入顯示懸浮窗的具體內容。透過setUIContent和showWindow介面載入顯示懸浮窗的具體內容。

4.      銷燬懸浮窗。當不再需要懸浮窗時,可根據具體實現邏輯,使用destroyWindow介面銷燬懸浮窗。

import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
    onWindowStageCreate(windowStage) {
        // 1.建立懸浮窗。
        let windowClass = null;
        let config = {name: "floatWindow", windowType: window.WindowType.TYPE_FLOAT, ctx: this.context};
        window.createWindow(config, (err, data) => {
            if (err.code) {
                console.error('Failed to create the floatWindow. Cause: ' + JSON.stringify(err));
                return;
            }
            console.info('Succeeded in creating the floatWindow. Data: ' + JSON.stringify(data));
            windowClass = data;
            // 2.懸浮窗視窗建立成功後,設定懸浮窗的位置、大小及相關屬性等。
            windowClass.moveWindowTo(300, 300, (err) => {
                if (err.code) {
                    console.error('Failed to move the window. Cause:' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in moving the window.');
            });
            windowClass.resize(500, 500, (err) => {
                if (err.code) {
                    console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in changing the window size.');
            });
            // 3.為懸浮窗載入對應的目標頁面。
            windowClass.setUIContent("pages/page4", (err) => {
                if (err.code) {
                    console.error('Failed to load the content. Cause:' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in loading the content.');
                // 3.顯示懸浮窗。
                windowClass.showWindow((err) => {
                    if (err.code) {
                        console.error('Failed to show the window. Cause: ' + JSON.stringify(err));
                        return;
                    }
                    console.info('Succeeded in showing the window.');
                });
            });
            // 4.銷燬懸浮窗。當不再需要懸浮窗時,可根據具體實現邏輯,使用destroy對其進行銷燬。
            windowClass.destroyWindow((err) => {
                if (err.code) {
                    console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err));
                    return;
                }
                console.info('Succeeded in destroying the window.');
            });
        });
    }
};


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70009402/viewspace-2986349/,如需轉載,請註明出處,否則將追究法律責任。

相關文章