【HarmonyOS Next】鴻蒙中自定義彈框OpenCustomDialog、CustomDialog與DialogHub的區別詳解
一、三者的區別與關系
1. 官方迭代過程為:
CustomDialog = 》 OpenCustomDialog = 》 DialogHub
迭代過程表明,彈框的調用越來越便捷,與UI解耦,最終達到在純邏輯中使用自定義彈出,彈框內容更新和生命周期可控,寫法簡潔。
2.CustomDialog的用法:
首先需要創建@CustomDialog裝飾的自定義彈框布局,CustomDialogController來實現彈窗彈出和關閉。
CustomDialogUI {// CustomDialog可直接獲取到dialogControllerdialogController: CustomDialogController;// 定義事件回調給外部使用onClose?: () => void;build() {Column() {Text('我是內容').fontSize(20)Button('Close').onClick(() => {// 點擊關閉彈框this.dialogController.close();if (this.onClose) {this.onClose()}}).backgroundColor(Color.White).fontColor(Color.Black)}.height(60).justifyContent(FlexAlign.Center)}
}
struct CustomDialogPage {// CustomDialog - CustomDialogController需在@Component內定義初始化。dialogController: CustomDialogController | null = new CustomDialogController({builder: CustomDialogUI({onClose: ()=> {console.info('Callback when the onClose button is clicked')},}),})build() {Column() {Button('click me').onClick(() => {this.dialogController.open()})}.width('100%').margin({ top: 5 })}
}
struct
綜上所述,CustomDialog 因為CustomDialogController強耦合于UI,需要在UI界面或者自定義View中使用CustomDialogController控制彈框顯示隱藏。無法在純邏輯類中處理彈框時機的顯示。(這種情況下只能想辦法發送通知給UI,UI再處理回調顯示,處理起來麻煩。)致命的問題是,彈框內的UI無法動態刷新。需要重新創建渲染。
3.OpenCustomDialog 的用法:
對標CustomDialog 的CustomDialogController。官方通過將彈框對象實例,放到上下文中,實現在純邏輯類中也可以調用彈框的顯示和隱藏。
將@CustomDialog彈框布局內容,放到ComponentContent節點對象中,實現彈框UI的解耦。
ComponentContentBuildText() {Column() {Text("測試數據").fontSize(50).fontWeight(FontWeight.Bold).margin({ bottom: 36 })}.backgroundColor('#FFF0F0F0')
}// OpenCustomDialog - ComponentContent // 建議整體抽個單例private contentNode: ComponentContent<Object> = new ComponentContent(this.getUIContext(), wrapBuilder(ComponentContentBuildText));this.getUIContext().getPromptAction().openCustomDialog(this.contentNode).then(() => {console.info('UpdateCustomDialog complete.')}).catch((error: BusinessError) => {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`onClickOpenCustomDialog args error code is ${code}, message is ${message}`);})
function
DialogHub的用法:
參考:【HarmonyOS Next】鴻蒙應用實現彈框DialogHub詳解
二、自定義View與UI解耦的解決方案:
目前共有三種方式,使用浮層(DialogHub底層原理),使用OpenCustomDialog,使用subWindow。
1.浮層
DialogHub底層原理。在頁面Page與彈框層之間,ArkUI框架有一個浮層。該層通過節點管控(增加,刪除)的方式,可以插入自定義UI。
ComponentContent可以理解為一個節點內容對象,在其中進行自定義UI的界面編寫,打包為一個ComponentContent節點,添加到浮層上,ArkUI框架就會加載顯示。
builderOverlay() {Column() {
}.focusable(false).width('100%').height('100%').hitTestBehavior(HitTestMode.Transparent)
}private overlayNode: OverlayManager = this.uiContext.getOverlayManager()let componentContent = new ComponentContent(this.uiContext, wrapBuilder<>(builderOverlay))this.overlayNode.addComponentContent(componentContent, 0)this.overlayContent.push(componentContent)
function
2.OpenCustomDialog
參考:【HarmonyOS Next】鴻蒙應用彈框和提示氣泡詳解(一)
3.subWindow
因為三方應用不能使用FloatWindow,沒有懸浮窗。只能通過子窗口SubWindow實現獨立的自定義View層級。
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
struct SubWinPage {private TAG: string = "SubWinPage";private sub_windowClass: window.Window | null = null;aboutToAppear() {this.showSubWindow()setTimeout(()=>{try {this.destroySubWindow();// window.getLastWindow(getContext()).then((win)=>{// console.error(this.TAG, 'win:' + JSON.stringify(win));// let height = win.getWindowDecorHeight();// console.error(this.TAG, 'height:' + height);// })let windowStage_: window.WindowStage = globalThis.mWindowStage;let win = windowStage_.getMainWindowSync();let height = win.getWindowDecorHeight();}catch (e){console.error(this.TAG, 'e:' + JSON.stringify(e));}},1000)}private showSubWindow() {console.log(this.TAG, 'showSubWindow start');let windowStage_: window.WindowStage = globalThis.mWindowStage;// 1.創建應用子窗口。if (windowStage_ == null) {console.error(this.TAG, 'Failed to create the subwindow. Cause: windowStage_ is null');}else {windowStage_.createSubWindow("mySubWindow", (err: BusinessError, data) => {let errCode: number = err.code;if (errCode) {console.error(this.TAG, 'Failed to create the subwindow. Cause: ' + JSON.stringify(err));return;}this.sub_windowClass = data;console.info(this.TAG, 'Succeeded in creating the subwindow. Data: ' + JSON.stringify(data));// 2.子窗口創建成功后,設置子窗口的位置、大小及相關屬性等。this.sub_windowClass.moveWindowTo(300, 300, (err: BusinessError) => {let errCode: number = err.code;if (errCode) {console.error(this.TAG, 'Failed to move the window. Cause:' + JSON.stringify(err));return;}console.info(this.TAG, 'Succeeded in moving the window.');});this.sub_windowClass.resize(500, 500, (err: BusinessError) => {let errCode: number = err.code;if (errCode) {console.error(this.TAG, 'Failed to change the window size. Cause:' + JSON.stringify(err));return;}console.info(this.TAG, 'Succeeded in changing the window size.');});// 3.為子窗口加載對應的目標頁面。this.sub_windowClass.setUIContent("pages/SubWinLoadPage", (err: BusinessError) => {let errCode: number = err.code;if (errCode) {console.error(this.TAG, 'Failed to load the content. Cause:' + JSON.stringify(err));return;}console.info(this.TAG, 'Succeeded in loading the content.');// 3.顯示子窗口。(this.sub_windowClass as window.Window).showWindow((err: BusinessError) => {let errCode: number = err.code;if (errCode) {console.error(this.TAG, 'Failed to show the window. Cause: ' + JSON.stringify(err));return;}console.info(this.TAG, 'Succeeded in showing the window.');});});})}console.log(this.TAG, 'showSubWindow end');}destroySubWindow() {// 4.銷毀子窗口。當不再需要子窗口時,可根據具體實現邏輯,使用destroy對其進行銷毀。(this.sub_windowClass as window.Window).destroyWindow((err: BusinessError) => {let errCode: number = err.code;if (errCode) {console.error(this.TAG, 'Failed to destroy the window. Cause: ' + JSON.stringify(err));return;}console.info(this.TAG, 'Succeeded in destroying the window.');});}build() {Column() {Text("點擊創建子窗口").id('SubWinPageHelloWorld').fontSize(50).fontWeight(FontWeight.Bold).onClick(()=>{this.showSubWindow();})Text("點擊銷毀子窗口").id('SubWinPageHelloWorld').fontSize(50).fontWeight(FontWeight.Bold).onClick(()=>{this.destroySubWindow();})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}
三、多彈框源碼示例:
import {DialogHub
} from "@hadss/dialoghub"
import { ComponentContent } from "@kit.ArkUI";
import { BusinessError } from "@kit.BasicServicesKit";
struct CustomDialogUI {// CustomDialog可直接獲取到dialogControllerdialogController: CustomDialogController;// 定義事件回調給外部使用onClose?: () => void;build() {Column() {Text('我是內容').fontSize(20)Button('Close').onClick(() => {// 點擊關閉彈框this.dialogController.close();if (this.onClose) {this.onClose()}}).backgroundColor(Color.White).fontColor(Color.Black)}.height(60).justifyContent(FlexAlign.Center)}
}
function ComponentContentBuildText() {Column() {Text("測試數據").fontSize(50).fontWeight(FontWeight.Bold).margin({ bottom: 36 })}.backgroundColor('#FFF0F0F0')
}/*** 彈框測試頁*/
struct DialogTestPage {// CustomDialog - CustomDialogController需在@Component內定義初始化。dialogController: CustomDialogController | null = new CustomDialogController({builder: CustomDialogUI({onClose: ()=> {console.info('Callback when the onClose button is clicked')},}),})// OpenCustomDialog - ComponentContent // 建議整體抽個單例private contentNode: ComponentContent<Object> = new ComponentContent(this.getUIContext(), wrapBuilder(ComponentContentBuildText));/*** 統一樣式封裝*/ ButtonStyle(){.width(px2vp(350)).height(px2vp(200)).margin({ top: px2vp(66) })}/*** 點擊顯示CustomDialog彈框 【官方不推薦】*/onClickCustomDialog = ()=>{this.dialogController?.open()}/*** 點擊顯示OpenCustomDialog*/onClickOpenCustomDialog = ()=>{this.getUIContext().getPromptAction().openCustomDialog(this.contentNode).then(() => {console.info('UpdateCustomDialog complete.')}).catch((error: BusinessError) => {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`onClickOpenCustomDialog args error code is ${code}, message is ${message}`);})}/*** 點擊顯示DialogHub彈框*/onClickDialogHub = ()=>{DialogHub.getToast().setTextContent("測試數據").setDuration(2000).build().show();}aboutToDisappear() {// 在自定義組件即將析構銷毀時將dialogController置空this.dialogController = null; // 將dialogController置空}build() {Column(){Button("customDialog").ButtonStyle().onClick(this.onClickCustomDialog)Button("openCustomDialog").ButtonStyle().onClick(this.onClickOpenCustomDialog)Button("dialogHub").ButtonStyle().onClick(this.onClickDialogHub)}.size({width: "100%",height: "100%"})}
}
{"name": "entry","version": "1.0.0","description": "Please describe the basic information.","main": "","author": "","license": "","dependencies": {"@hadss/dialoghub": "^1.0.0-rc.1"}
}