HarmonyOS鸿蒙Next中隐私协议弹窗实现(跳转返回不消失 + 侧滑拦截)
HarmonyOS鸿蒙Next中隐私协议弹窗实现(跳转返回不消失 + 侧滑拦截) 隐私协议弹窗实现(跳转返回不消失 + 侧滑拦截)
最简单的就是使用CustomDialogController 实现:
dialogController: CustomDialogController = new CustomDialogController({
builder: PrivacyDialog({ //自定义弹框页面
cancel: () => {
//自定义回调方法
},
confirm: () => {
//自定义回调方法
},
}),
autoCancel: false, //不能跳过、侧滑关闭
onWillDismiss: (dismissDialogAction: DismissDialogAction) => {
console.log('dialog onWillDismiss reason: ' + dismissDialogAction.reason);
// 1、PRESS_BACK 点击三键back、左滑/右滑、键盘ESC。
// 2、TOUCH_OUTSIDE 点击遮障层时
// 3、CLOSE_BUTTON 点击关闭按钮
if (dismissDialogAction.reason === DismissReason.PRESS_BACK) {
//进入后台
let context = getContext(this) as common.UIAbilityContext;
context.terminateSelf();
}
},
})
你只需要自定义一下弹窗的页面就行了~~~
更多关于HarmonyOS鸿蒙Next中隐私协议弹窗实现(跳转返回不消失 + 侧滑拦截)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
1. 实现原理
我们可以通过levelMode: LevelMode.EMBEDDED将弹窗挂载到指定页面节点,确保页面跳转后弹窗不销毁;利用onWillDismiss回调拦截侧滑、点击遮罩等关闭行为,强制用户点击 “同意 / 拒绝” 按钮。
2. 封装工具类(封装弹窗打开/关闭/参数更新逻辑,支持自定义内容、样式、交互)
import { UIContext, ComponentContent, DialogOptions, DismissDialogAction, LevelMode } from '@ohos/ui';
/**
* 弹窗管理工具类(适配HarmonyOS 5.0+ UIContext弹窗)
* 封装弹窗打开/关闭/参数更新逻辑,支持自定义内容、样式、交互
*/
export class PromptActionClass {
// 静态属性:全局唯一实例
private static instance: PromptActionClass;
// 弹窗核心配置
private uiContext: UIContext | null = null;
private contentNode: ComponentContent | null = null;
private dialogOptions: DialogOptions = {};
private dialogId: string | null = null; // 弹窗唯一标识
/**
* 单例模式:获取工具类实例
*/
public static getInstance(): PromptActionClass {
if (!PromptActionClass.instance) {
PromptActionClass.instance = new PromptActionClass();
}
return PromptActionClass.instance;
}
/**
* 设置UIContext(必须先调用,否则弹窗无法挂载)
* @param context 页面/组件的UIContext
*/
public static setContext(context: UIContext): void {
this.getInstance().uiContext = context;
}
/**
* 设置弹窗内容节点
* @param node 自定义弹窗内容(ComponentContent类型)
*/
public static setContentNode(node: ComponentContent): void {
this.getInstance().contentNode = node;
}
/**
* 设置弹窗配置项
* @param options 弹窗样式/交互配置(DialogOptions)
*/
public static setOptions(options: DialogOptions): void {
const instance = this.getInstance();
// 默认配置合并用户配置
instance.dialogOptions = {
alignment: DialogAlignment.Center, // 默认居中
isModal: true, // 默认模态弹窗
levelMode: LevelMode.APPLICATION, // 默认应用层级
autoCancel: false, // 默认不允许点击遮罩关闭
...options
};
}
/**
* 打开弹窗(核心方法)
* @returns Promise<string> 弹窗ID
*/
public static async openDialog(): Promise<string> {
const instance = this.getInstance();
// 参数校验
if (!instance.uiContext) {
throw new Error('未设置UIContext,请先调用setContext()');
}
if (!instance.contentNode) {
throw new Error('未设置弹窗内容,请先调用setContentNode()');
}
try {
// 调用UIContext弹窗API打开弹窗
instance.dialogId = await instance.uiContext.openCustomDialog(
instance.contentNode,
instance.dialogOptions
);
console.log(`弹窗打开成功,ID:${instance.dialogId}`);
return instance.dialogId;
} catch (err) {
console.error('弹窗打开失败:', err);
throw err;
}
}
/**
* 关闭弹窗
* @param dialogId 弹窗ID(不传则关闭当前实例弹窗)
*/
public static async closeDialog(dialogId?: string): Promise<void> {
const instance = this.getInstance();
const targetId = dialogId || instance.dialogId;
if (!instance.uiContext || !targetId) {
console.warn('弹窗未打开或UIContext未初始化');
return;
}
try {
await instance.uiContext.closeCustomDialog(targetId);
console.log(`弹窗关闭成功,ID:${targetId}`);
instance.dialogId = null; // 清空弹窗ID
} catch (err) {
console.error('弹窗关闭失败:', err);
throw err;
}
}
/**
* 更新弹窗内容(支持动态刷新)
* @param data 新的弹窗数据(需与内容节点绑定的参数匹配)
*/
public static updateContent(data: any): void {
const instance = this.getInstance();
if (!instance.contentNode) {
console.warn('未设置弹窗内容,无法更新');
return;
}
// 调用ComponentContent的update方法刷新内容
instance.contentNode.update(data);
console.log('弹窗内容更新成功');
}
/**
* 检查弹窗是否已打开
* @returns boolean
*/
public static isDialogOpen(): boolean {
return !!this.getInstance().dialogId;
}
/**
* 销毁弹窗实例(释放资源)
*/
public static destroy(): void {
const instance = this.getInstance();
if (instance.dialogId) {
this.closeDialog(instance.dialogId);
}
instance.uiContext = null;
instance.contentNode = null;
instance.dialogOptions = {};
instance.dialogId = null;
}
}
/**
* 弹窗对齐方式枚举(补充系统未导出的枚举)
*/
export enum DialogAlignment {
Top = 0,
Center = 1,
Bottom = 2,
Left = 3,
Right = 4,
TopLeft = 5,
TopRight = 6,
BottomLeft = 7,
BottomRight = 8
}
/**
* 快速创建ComponentContent(简化内容节点构建)
* @param builder 弹窗内容构建器
* @param initData 初始数据
* @returns ComponentContent
*/
export function wrapBuilder<T = any>(builder: (data: T) => JSX.Element, initData?: T): ComponentContent {
const contentNode = new ComponentContent();
// 初始化内容
if (initData) {
contentNode.update(initData);
}
// 绑定构建函数
contentNode.setBuilder(builder);
return contentNode;
}
3. 实现完整代码
// 1. 导入封装类PromptActionClass工具类
import { PromptActionClass } from '../utils/PromptActionClass';
import { LevelMode, DismissDialogAction } from '@ohos/ui';
@Entry
@Component
struct PrivacyPage {
private pageStack = router.getLength() > 0 ? router : undefined;
build() {
Column() {
// 触发弹窗的按钮(绑定页面节点ID)
Button('打开隐私协议')
.id('privacyTriggerBtn') // 关键:用于定位挂载页面
.fontSize(16)
.width('80%')
.borderRadius(20)
.backgroundColor('#0A59F7')
.margin({ top: 100 })
.onClick(() => this.openPrivacyDialog())
// 协议详情跳转入口(演示弹窗不消失场景)
Text('查看完整隐私协议')
.fontSize(14)
.fontColor('#0A59F7')
.margin({ top: 20 })
.onClick(() => {
// 跳转协议页面,弹窗保持显示
router.pushUrl({ url: 'pages/PrivacyDetail' });
})
}
.width('100%')
.height('100%')
.padding(16)
}
// 打开隐私弹窗核心方法
private openPrivacyDialog() {
// 获取绑定的页面节点
const triggerNode = this.getUIContext().getFrameNodeById('privacyTriggerBtn');
if (!triggerNode) return;
// 1. 构建弹窗内容(协议说明+同意/拒绝按钮)
const privacyContent = (
Column() {
Text('隐私政策说明')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
Text('为保障您的使用体验,我们需要获取设备权限,详细条款见隐私协议')
.fontSize(14)
.margin({ bottom: 20 })
.width('100%')
Row() {
Button('拒绝')
.fontSize(14)
.width('45%')
.borderRadius(16)
.backgroundColor('#F1F3F5')
.onClick(() => PromptActionClass.closeDialog())
Button('同意')
.fontSize(14)
.width('45%')
.borderRadius(16)
.backgroundColor('#0A59F7')
.marginLeft('10%')
.onClick(() => {
PromptActionClass.closeDialog();
// 后续权限申请逻辑
})
}
}
.width('85%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
);
// 2. 配置弹窗参数(关键:页面挂载+侧滑拦截)
const uiContext = this.getUIContext();
PromptActionClass.setContext(uiContext);
PromptActionClass.setContentNode(privacyContent);
PromptActionClass.setOptions({
levelMode: LevelMode.EMBEDDED, // 挂载到指定页面
levelUniqueId: triggerNode.getUniqueId(), // 绑定节点ID
isModal: true, // 模态弹窗,遮罩层不可点击
alignment: DialogAlignment.Center, // 居中显示
// 侧滑/遮罩关闭拦截
onWillDismiss: (action: DismissDialogAction) => {
// 拦截所有非按钮触发的关闭行为
if (action.reason !== 'manual') {
return false; // 阻止弹窗关闭
}
return true;
}
});
// 3. 打开弹窗
PromptActionClass.openDialog();
}
}
4. 效果说明
点击 “打开隐私协议” 弹出居中弹窗,遮罩层半透明,点击遮罩或侧滑不会关闭;
点击 “查看完整隐私协议” 跳转新页面,返回后弹窗仍保持原状态;
仅能通过 “同意 / 拒绝” 按钮关闭弹窗,确保用户阅读协议。
你这没有CustomDialogController 使用简单啊,
有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html
谢谢回答,简单做,确实可以直接用CustomDialog。但是项目若需要品牌化设计或者适配复杂场景,自定义要方便改动一些,
在HarmonyOS Next中实现隐私协议弹窗需使用ArkUI的模态弹窗机制,通过@CustomDialog装饰器创建自定义弹窗组件。设置isShow属性控制弹窗显隐,结合onWillDismiss回调处理跳转返回时的状态保持。侧滑拦截通过弹窗的gesture属性配置,禁用边缘滑动手势。页面路由使用router.pushUrl的singleTask模式避免重复实例化,确保弹窗状态持久化。具体实现需在aboutToAppear生命周期初始化弹窗状态,通过@State装饰器管理显示状态。
在HarmonyOS Next中实现隐私协议弹窗,需要结合页面生命周期和手势拦截机制:
-
跳转返回不消失:
- 使用
Page组件的aboutToAppear和aboutToDisappear生命周期 - 在
aboutToDisappear中保存弹窗状态,在aboutToAppear中恢复显示 - 通过
@State装饰器管理弹窗可见性状态
- 使用
-
侧滑拦截:
- 使用
gesture属性配置滑动手势 - 通过
onTouch事件监听滑动操作 - 设置
gestureMask为GestureMask.Ignore防止手势冲突
- 使用
示例代码:
@Entry
@Component
struct PrivacyDialogPage {
@State isDialogShow: boolean = true
aboutToAppear() {
// 恢复弹窗显示状态
if (/* 需要显示的条件 */) {
this.isDialogShow = true
}
}
aboutToDisappear() {
// 保存弹窗状态到AppStorage
AppStorage.setOrCreate('privacyDialogShow', this.isDialogShow)
}
build() {
Column() {
// 页面内容
if (this.isDialogShow) {
PrivacyDialog()
.onTouch((event: TouchEvent) => {
// 拦截侧滑手势
if (event.type === TouchType.Move) {
// 处理滑动逻辑
}
})
}
}
.gesture(
PanGesture({ direction: PanDirection.Horizontal })
.onActionStart(() => {
// 手势开始时拦截
})
)
}
}
关键点:
- 使用AppStorage持久化弹窗状态
- 通过手势掩码控制手势优先级
- 合理管理弹窗组件的生命周期

