目錄
不依賴UI組件的全局氣泡提示 (openPopup)
彈出氣泡
創建ComponentContent
綁定組件信息
設置彈出氣泡樣式
更新氣泡樣式
關閉氣泡
在HAR包中使用全局氣泡提示
不依賴UI組件的全局菜單 (openMenu)
彈出菜單
創建ComponentContent
綁定組件信息
設置彈出菜單樣式
更新菜單樣式
關閉菜單
在HAR包中使用全局菜單
Toast
使用建議
即時反饋模式對比
創建即時反饋
顯示關閉即時反饋
不依賴UI組件的全局氣泡提示 (openPopup)
氣泡提示(Popup)在使用時依賴綁定UI組件,否則無法使用。從API version 18開始,可以通過使用全局接口openPopup的方式,在無UI組件的場景下直接或封裝使用,例如在事件回調中使用或封裝后對外提供能力。
彈出氣泡
通過openPopup可以彈出氣泡。
promptAction.openPopup(contentNode, { id: targetId }, {enableArrow: true
}).then(() => {console.info('openPopup success');}).catch((err: BusinessError) => {console.error('openPopup error: ' + err.code + ' ' + err.message);})
創建ComponentContent
通過調用openPopup接口彈出其氣泡,需要提供用于定義自定義彈出框的內容ComponentContent。其中,wrapBuilder(buildText)封裝自定義組件,new Params(this.message)是自定義組件的入參,可以缺省,也可以傳入基礎數據類型。
private contentNode: ComponentContent<Object> = new ComponentContent(uiContext, wrapBuilder(buildText), this.message);
如果在wrapBuilder中包含其他組件(例如:Popup、Chip組件),則ComponentContent應采用帶有四個參數的構造函數constructor,其中options參數應傳遞{ nestingBuilderSupported: true }。
@Builder
export function buildText(params: Params) {Popup({// 類型設置圖標內容icon: {image: $r('app.media.app_icon'),width: 32,height: 32,fillColor: Color.White,borderRadius: 10} as PopupIconOptions,// 設置文字內容title: {text: `This is a Popup title 1`,fontSize: 20,fontColor: Color.Black,fontWeight: FontWeight.Normal} as PopupTextOptions,// 設置文字內容message: {text: `This is a Popup message 1`,fontSize: 15,fontColor: Color.Black} as PopupTextOptions,// 設置按鈕內容buttons: [{text: 'confirm',action: () => {console.info('confirm button click');},fontSize: 15,fontColor: Color.Black,},{text: 'cancel',action: () => {console.info('cancel button click');},fontSize: 15,fontColor: Color.Black},] as [PopupButtonOptions?, PopupButtonOptions?]})
}let contentNode: ComponentContent<Object> = new ComponentContent(uiContext, wrapBuilder(buildText), this.message, { nestingBuilderSupported: true });
綁定組件信息
通過調用openPopup接口彈出氣泡,需要提供綁定組件的信息TargetInfo。若未傳入有效的target,則無法彈出氣泡。
let frameNode: FrameNode | null = this.ctx.getFrameNodeByUniqueId(this.getUniqueId());
let targetId = frameNode?.getChild(0)?.getUniqueId();
設置彈出氣泡樣式
通過調用openPopup接口彈出氣泡,可以設置PopupCommonOptions屬性調整氣泡樣式。
private options: PopupCommonOptions = { enableArrow: true };
更新氣泡樣式
通過updatePopup可以更新氣泡的樣式。支持全量更新和增量更新其氣泡樣式,不支持更新showInSubWindow、focusable、onStateChange、onWillDismiss和transition。
promptAction.updatePopup(contentNode, {enableArrow: false
}, true).then(() => {console.info('updatePopup success');}).catch((err: BusinessError) => {console.error('updatePopup error: ' + err.code + ' ' + err.message);})
關閉氣泡
通過調用closePopup可以關閉氣泡。
promptAction.closePopup(contentNode).then(() => {console.info('closePopup success');}).catch((err: BusinessError) => {console.error('closePopup error: ' + err.code + ' ' + err.message);})
由于updatePopup和closePopup依賴content來更新或者關閉指定的氣泡,開發者需自行維護傳入的content。
在HAR包中使用全局氣泡提示
以下示例通過HAR包封裝一個Popup,從而對外提供氣泡的彈出、更新和關閉能力。
// library/src/main/ets/components/MainPage.etsimport { BusinessError } from '@kit.BasicServicesKit';
import { ComponentContent, TargetInfo, PromptAction } from '@kit.ArkUI';export class PromptActionClass {private promptAction: PromptAction | null = null;private contentNode: ComponentContent<Object> | null = null;private options: PopupCommonOptions | null = null;private target: TargetInfo | null = null;private isPartialUpdate: boolean = false;public setPromptAction(promptAction: PromptAction) {this.promptAction = promptAction;}public setContentNode(node: ComponentContent<Object>) {this.contentNode = node;}public setTarget(target: TargetInfo) {this.target = target;}public setOptions(options: PopupCommonOptions) {this.options = options;}public setIsPartialUpdate(isPartialUpdate: boolean) {this.isPartialUpdate = isPartialUpdate;}public openPopup() {if (this.promptAction != null) {this.promptAction.openPopup(this.contentNode, this.target, this.options).then(() => {console.info('openPopup success');}).catch((err: BusinessError) => {console.error('openPopup error: ' + err.code + ' ' + err.message);})}}public closePopup() {if (this.promptAction != null) {this.promptAction.closePopup(this.contentNode).then(() => {console.info('closePopup success');}).catch((err: BusinessError) => {console.error('closePopup error: ' + err.code + ' ' + err.message);})}}public updatePopup(options: PopupCommonOptions) {if (this.promptAction != null) {this.promptAction.updatePopup(this.contentNode, options, this.isPartialUpdate).then(() => {console.info('updatePopup success');}).catch((err: BusinessError) => {console.error('updatePopup error: ' + err.code + ' ' + err.message);})}}
}
// entry/src/main/ets/pages/Index.etsimport { PromptActionClass } from "library";
import { ComponentContent, PromptAction } from '@kit.ArkUI';class Params {text: string = "";promptActionClass: PromptActionClass = new PromptActionClass();constructor(text: string, promptActionClass: PromptActionClass) {this.text = text;this.promptActionClass = promptActionClass;}
}@Builder
function buildText(params: Params) {Column() {Text(params.text).fontSize(20).margin({ top: 10 })Button('Update').margin({ top: 10 }).width(100).onClick(() => {params.promptActionClass.updatePopup({enableArrow: false,});})Button('Close').margin({ top: 10 }).width(100).onClick(() => {params.promptActionClass.closePopup();})}.width(130).height(150)
}@Entry
@Component
struct Index {@State message: string = "hello";private uiContext: UIContext = this.getUIContext();private promptAction: PromptAction = this.uiContext.getPromptAction();private promptActionClass: PromptActionClass = new PromptActionClass();private targetId: number = 0;private contentNode: ComponentContent<Object> =new ComponentContent(this.uiContext, wrapBuilder(buildText), new Params(this.message, this.promptActionClass));private options: PopupCommonOptions = { enableArrow: true };build() {Column() {Button("openPopup").margin({ top: 50, left: 100 }).onClick(() => {let frameNode: FrameNode | null = this.uiContext.getFrameNodeByUniqueId(this.getUniqueId());let targetId = frameNode?.getChild(0)?.getUniqueId();if (targetId == undefined) {this.targetId = 0;} else {this.targetId = targetId;}this.promptActionClass.setPromptAction(this.promptAction);this.promptActionClass.setContentNode(this.contentNode);this.promptActionClass.setOptions(this.options);this.promptActionClass.setIsPartialUpdate(false);this.promptActionClass.setTarget({ id: this.targetId });this.promptActionClass.openPopup();})}}
}
?
不依賴UI組件的全局菜單 (openMenu)
菜單控制 (Menu)在使用時依賴綁定UI組件,否則無法使用。從API version 18開始,可以通過使用全局接口openMenu的方式,在無UI組件的場景下直接或封裝使用,例如在事件回調中使用或封裝后對外提供能力。
彈出菜單
通過openMenu可以彈出菜單。
promptAction.openMenu(contentNode, { id: targetId }, {enableArrow: true
}).then(() => {console.info('openMenu success');}).catch((err: BusinessError) => {console.error('openMenu error: ' + err.code + ' ' + err.message);})
創建ComponentContent
通過調用openMenu接口彈出菜單,需要提供用于定義自定義彈出框的內容ComponentContent。其中,wrapBuilder(buildText)封裝自定義組件,new Params(this.message)是自定義組件的入參,可以缺省,也可以傳入基礎數據類型。
private contentNode: ComponentContent<Object> = new ComponentContent(uiContext, wrapBuilder(buildText), this.message);
如果在wrapBuilder中包含其他組件(例如:Popup、Chip組件),則ComponentContent應采用帶有四個參數的構造函數constructor,其中options參數應傳遞{ nestingBuilderSupported: true }。
@Builder
export function buildText(params: Params) {Menu({// 類型設置圖標內容icon: {image: $r('app.media.app_icon'),width: 32,height: 32,fillColor: Color.White,borderRadius: 10} as MenuIconOptions,// 設置文字內容title: {text: `This is a Menu title 1`,fontSize: 20,fontColor: Color.Black,fontWeight: FontWeight.Normal} as MenuTextOptions,// 設置文字內容message: {text: `This is a Menu message 1`,fontSize: 15,fontColor: Color.Black} as MenuTextOptions,// 設置按鈕內容buttons: [{text: 'confirm',action: () => {console.info('confirm button click');},fontSize: 15,fontColor: Color.Black,},{text: 'cancel',action: () => {console.info('cancel button click');},fontSize: 15,fontColor: Color.Black},] as [MenuButtonOptions?, MenuButtonOptions?]})
}let contentNode: ComponentContent<Object> = new ComponentContent(uiContext, wrapBuilder(buildText), this.message, { nestingBuilderSupported: true });
綁定組件信息
通過調用openMenu接口彈出菜單,需要提供綁定組件的信息TargetInfo。若未傳入有效的target,則無法彈出菜單。
let frameNode: FrameNode | null = this.ctx.getFrameNodeByUniqueId(this.getUniqueId());
let targetId = frameNode?.getChild(0)?.getUniqueId();
設置彈出菜單樣式
通過調用openMenu接口彈出菜單,可以設置MenuOptions屬性調整菜單樣式。title屬性不生效。preview參數僅支持設置MenuPreviewMode類型。
private options: MenuOptions = { enableArrow: true, placement: Placement.Bottom };
更新菜單樣式
通過updateMenu可以更新菜單的樣式。支持全量更新和增量更新其菜單樣式,不支持更新showInSubWindow、preview、previewAnimationOptions、transition、onAppear、aboutToAppear、onDisappear和aboutToDisappear。
promptAction.updateMenu(contentNode, {enableArrow: false
}, true).then(() => {console.info('updateMenu success');}).catch((err: BusinessError) => {console.error('updateMenu error: ' + err.code + ' ' + err.message);})
關閉菜單
通過調用closeMenu可以關閉菜單。
promptAction.closeMenu(contentNode).then(() => {console.info('openMenu success');}).catch((err: BusinessError) => {console.error('openMenu error: ' + err.code + ' ' + err.message);})
由于updateMenu和closeMenu依賴content來更新或者關閉指定的菜單,開發者需自行維護傳入的content。
在HAR包中使用全局菜單
可以通過HAR包封裝一個Menu,從而對外提供菜單的彈出、更新和關閉能力。
Toast
即時反饋(Toast)是一種臨時性的消息提示框,用于向用戶顯示簡短的操作反饋或狀態信息。?它通常在屏幕的底部或頂部短暫彈出,隨后在一段時間后自動消失。即時反饋的主要目的是提供簡潔、不打擾的信息反饋,避免干擾用戶當前的操作流程。
可以通過使用UIContext中的getPromptAction方法獲取當前UI上下文關聯的PromptAction對象,再通過該對象調用showToast創建并顯示文本提示框。
為了安全考慮,例如Toast惡意遮擋其他頁面,Toast只能顯示在當前的UI實例中,應用退出后,不會單獨顯示在桌面上。
使用建議
- 合理使用彈出場景,避免過度提醒用戶。
- 可以針對以下常用場景使用即時反饋操作,例如,當用戶執行某個操作時及時結果反饋,用來提示用戶操作是否成功或失敗;或是當應用程序的狀態發生變化時提供狀態更新等。
- 注意文本的信息密度,即時反饋展示時間有限,應當避免長文本的出現。
- Toast控件的文本應該清晰可讀,字體大小和顏色應該與應用程序的主題相符。除此之外,即時反饋控件本身不應該包含任何可交互的元素,如按鈕或鏈接。
- 杜絕強制占位和密集彈出的提示。
- 即時反饋作為應用內的輕量通知,應當避免內容布局占用界面內的其他元素信息,如遮蓋彈出框的展示內容,從而迷惑用戶彈出的內容是否屬于彈出框。再或者頻繁性的彈出信息內容,且每次彈出之間無時間間隔,影響用戶的正常使用。也不要在短時間內頻繁彈出新的即時反饋替代上一個。即時反饋的單次顯示時長不要超過 3 秒鐘,避免影響用戶正常的行為操作。
- 遵從系統默認彈出位置。
- 即時反饋在系統中默認從界面底部彈出,距離底部有一定的安全間距,作為系統性的應用內提示反饋,請遵守系統默認效果,避免與其他彈出類組件內容重疊。特殊場景下可對內容布局進行規避。
即時反饋模式對比
即時反饋提供了兩種顯示模式,分別為DEFAULT(顯示在應用內)、TOP_MOST(顯示在應用之上)。
在TOP_MOST類型的Toast顯示前,會創建一個全屏大小的子窗(手機上子窗大小和主窗大小一致),然后在該子窗上計算Toast的布局位置,最后顯示在該子窗上。具體和DEFAULT模式Toast的差異如下:
差異點 | DEFAULT | TOP_MOST |
---|---|---|
是否創建子窗 | 否 | 是 |
層級 | 顯示在主窗內,層級和主窗一致,一般比較低 | 顯示在子窗中,一般比主窗層級高,比其他彈窗類組件層級高,比軟鍵盤和權限彈窗層級低 |
是否避讓軟鍵盤 | 軟鍵盤抬起時,必定上移軟鍵盤的高度 | 軟鍵盤抬起時,只有toast被遮擋時,才會避讓,且避讓后toast底部距離軟鍵盤高度為80vp |
UIExtension內布局 | 以UIExtension為主窗中布局,對齊方式與UIExtension對齊 | 以宿主窗口為主窗中布局,對齊方式與宿主窗口對齊 |
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {build() {Column({ space: 10 }) {TextInput()Button() {Text("DEFAULT類型Toast").fontSize(20).fontWeight(FontWeight.Bold)}.width('100%').onClick(() => {this.getUIContext().getPromptAction().showToast({message: "ok,我是DEFAULT toast",duration: 2000,showMode: promptAction.ToastShowMode.DEFAULT,bottom: 80});})Button() {Text("TOPMOST類型Toast").fontSize(20).fontWeight(FontWeight.Bold)}.width('100%').onClick(() => {this.getUIContext().getPromptAction().showToast({message: "ok,我是TOP_MOST toast",duration: 2000,showMode: promptAction.ToastShowMode.TOP_MOST,bottom: 85});})}}
}
創建即時反饋
適用于短時間內提示框自動消失的場景
import { PromptAction } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';@Entry
@Component
struct toastExample {private uiContext: UIContext = this.getUIContext();private promptAction: PromptAction = this.uiContext.getPromptAction();build() {Column() {Button('Show toast').fontSize(20).onClick(() => {try {this.promptAction.showToast({message: 'Hello World',duration: 2000})} catch (error) {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`showToast args error code is ${code}, message is ${message}`);};})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}
顯示關閉即時反饋
適用于提示框提留時間較長,用戶操作可以提前關閉提示框的場景。
import { LengthMetrics, PromptAction } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';@Entry
@Component
struct toastExample {@State toastId: number = 0;private uiContext: UIContext = this.getUIContext();private promptAction: PromptAction = this.uiContext.getPromptAction();build() {Column() {Button('Open Toast').type(ButtonType.Capsule).height(100).onClick(() => {try {this.promptAction.openToast({message: 'Toast Massage',duration: 10000,}).then((toastId: number) => {this.toastId = toastId;});} catch (error) {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`OpenToast error code is ${code}, message is ${message}`);};})Blank().height(50);Button('Close Toast').height(100).type(ButtonType.Capsule).onClick(() => {try {this.promptAction.closeToast(this.toastId);} catch (error) {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`CloseToast error code is ${code}, message is ${message}`);};})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}