文章目录
- [1 -> 概述:闪控球------轻量级交互的新范式](#1 -> 概述:闪控球——轻量级交互的新范式)
- [2 -> 前置准备:能力检测与权限声明](#2 -> 前置准备:能力检测与权限声明)
-
- [2.1 -> 设备能力检测](#2.1 -> 设备能力检测)
- [2.2 -> 权限声明](#2.2 -> 权限声明)
- [3 -> 核心API详解:从创建到销毁](#3 -> 核心API详解:从创建到销毁)
-
- [3.1 -> 创建闪控球控制器:`floatingBall.create()`](#3.1 -> 创建闪控球控制器:
floatingBall.create()) - [3.2 -> 启动闪控球:`startFloatingBall()`](#3.2 -> 启动闪控球:
startFloatingBall()) - [3.3 -> 更新闪控球内容:`updateFloatingBall()`](#3.3 -> 更新闪控球内容:
updateFloatingBall()) - [3.4 -> 停止闪控球:`stopFloatingBall()`](#3.4 -> 停止闪控球:
stopFloatingBall())
- [3.1 -> 创建闪控球控制器:`floatingBall.create()`](#3.1 -> 创建闪控球控制器:
- [4 -> 交互与生命周期管理:事件监听](#4 -> 交互与生命周期管理:事件监听)
-
- [4.1 -> 监听生命周期状态:`on('stateChange')`](#4.1 -> 监听生命周期状态:
on('stateChange')) - [4.2 -> 监听点击事件:`on('click')`](#4.2 -> 监听点击事件:
on('click')) - [4.3 -> 恢复主窗口:`restoreMainWindow()`](#4.3 -> 恢复主窗口:
restoreMainWindow()) - [4.4 -> 获取窗口信息:`getFloatingBallWindowInfo()`](#4.4 -> 获取窗口信息:
getFloatingBallWindowInfo())
- [4.1 -> 监听生命周期状态:`on('stateChange')`](#4.1 -> 监听生命周期状态:
- [5 -> 完整实战示例:比价助手闪控球](#5 -> 完整实战示例:比价助手闪控球)
- [6 -> 总结与展望](#6 -> 总结与展望)

1 -> 概述:闪控球------轻量级交互的新范式
在移动应用生态日益成熟的今天,用户对于"即用即走"的轻量级交互体验需求愈发强烈。无论是电商比价、学习搜题,还是外卖抢单,用户都希望在不中断当前任务的前提下,快速获取关键信息或执行核心操作。鸿蒙6.0 ArkUI 新增的**闪控球(Floating Ball)**功能,正是为此类场景量身定制的解决方案。
闪控球以悬浮小组件的形式展现于其他应用之上,其形态类似于一个精致的"小球"或"胶囊",能够即时呈现应用的关键信息(如比价结果、题目答案、抢单倒计时),并通过点击等交互快速拉起应用主窗口。相较于传统的悬浮窗或通知栏,闪控球具备以下显著优势:
- 轻量化:占用屏幕空间极小,几乎不干扰用户当前操作。
- 高优先级展示:始终置顶显示,确保关键信息第一时间触达用户。
- 交互闭环:支持状态监听与点击响应,可无缝衔接应用主界面。
- 系统级能力:由窗口管理服务(SystemCapability.Window.SessionManager)统一调度,具备良好的性能与稳定性。
从技术实现角度看,鸿蒙为开发者提供了@ohos.window.floatingBall模块,该模块自API version 20起正式开放。通过一套简洁的API,开发者可以完成从设备兼容性检测、闪控球创建、启动/更新/停止,到生命周期监听、点击事件处理、主窗口恢复等一系列操作。本文将围绕这一核心模块,结合官方文档与实战经验,系统讲解闪控球功能的集成与最佳实践。
2 -> 前置准备:能力检测与权限声明
在正式开发之前,有两项基础工作必须完成:判断设备是否支持闪控球功能 ,以及声明必要的权限。
2.1 -> 设备能力检测
由于闪控球功能依赖于具体的硬件与系统版本,直接调用API可能导致异常。因此,官方强烈建议在调用任何闪控球接口前,使用floatingBall.isFloatingBallEnabled()进行能力检测。
typescript
import { floatingBall } from '@kit.ArkUI';
// 判断当前设备是否支持闪控球功能
const isEnabled: boolean = floatingBall.isFloatingBallEnabled();
if (!isEnabled) {
console.warn('当前设备不支持闪控球功能');
// 可在此处降级处理,如使用普通弹窗或通知
}
该方法返回true时,才可安全调用后续API。该检测应在应用启动时或相关功能入口处执行。
2.2 -> 权限声明
启动闪控球以及恢复主窗口需要ohos.permission.USE_FLOAT_BALL权限。该权限属于user_grant(用户授权)类型,需要在module.json5中声明,并在运行时动态请求。
module.json5配置示例:
json
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.USE_FLOAT_BALL",
"reason": "$string:float_ball_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
权限请求时机建议放在首次调用startFloatingBall之前,通过abilityAccessCtrl向用户申请。
3 -> 核心API详解:从创建到销毁
闪控球的操作流程可概括为"创建控制器 → 启动 → 更新(可选) → 监听交互 → 停止"。下面逐一拆解。
3.1 -> 创建闪控球控制器:floatingBall.create()
控制器(FloatingBallController)是所有后续操作的基础。创建时需要传入一个FloatingBallConfiguration对象,其中最关键的是context参数------它必须是UIAbilityContext,通常通过this.getUIContext().getHostContext()在组件内获取。
typescript
import { floatingBall } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
let floatingBallController: floatingBall.FloatingBallController | undefined;
// 在UI组件内获取Context
let ctx = this.getUIContext().getHostContext() as common.UIAbilityContext;
let config: floatingBall.FloatingBallConfiguration = {
context: ctx,
};
try {
floatingBall.create(config)
.then((controller: floatingBall.FloatingBallController) => {
floatingBallController = controller;
console.info('闪控球控制器创建成功');
})
.catch((err: BusinessError) => {
console.error(`创建控制器失败: ${err.code}, ${err.message}`);
});
} catch (error) {
console.error(`创建控制器异常: ${error.code}, ${error.message}`);
}
注意 :
create方法在Phone和Tablet设备上正常调用,在其他设备上会返回801错误码(能力不支持)。因此,务必在调用前使用isFloatingBallEnabled判断。
3.2 -> 启动闪控球:startFloatingBall()
获取控制器后,通过startFloatingBall方法启动闪控球。该方法需要传入FloatingBallParams,其中template(模板类型)和title(标题)为必填项。
模板类型详解:
STATIC:静态布局,支持标题和图标。需同时传入title和icon。NORMAL:普通文本布局,显示标题+内容。EMPHATIC:强调文本布局,显示图标+标题+内容。SIMPLE:纯文本布局,仅显示标题。
typescript
import { image } from '@kit.ImageKit';
// 准备图标资源(PixelMap)
let pixelMap: image.PixelMap | undefined;
// 此处省略从资源或网络加载PixelMap的代码...
let startParams: floatingBall.FloatingBallParams = {
template: floatingBall.FloatingBallTemplate.EMPHATIC, // 选择强调文本布局
title: '比价助手',
content: '当前商品最低价: ¥1299',
backgroundColor: '#FF008EF5', // 十六进制颜色,支持8位ARGB
icon: pixelMap, // 可选,建议128*128像素
};
try {
floatingBallController?.startFloatingBall(startParams)
.then(() => {
console.info('闪控球已启动');
})
.catch((err: BusinessError) => {
console.error(`启动失败: ${err.code}, ${err.message}`);
});
} catch (error) {
console.error(`启动异常: ${error.code}, ${error.message}`);
}
参数约束:
title不可为空字符串,且大小不超过64字节。content可选,不超过64字节,默认为空。icon为PixelMap类型,像素总字节数不超过192KB。- 不同模板对参数要求不同,例如
STATIC模板下icon为必传,而SIMPLE模板则只能传title。
3.3 -> 更新闪控球内容:updateFloatingBall()
当闪控球已启动后,可通过updateFloatingBall动态更新其显示内容。这在比价结果变动、抢单倒计时等场景中非常实用。
typescript
let updateParams: floatingBall.FloatingBallParams = {
template: floatingBall.FloatingBallTemplate.EMPHATIC, // 注意:模板类型不可更改
title: '比价助手',
content: '最新低价: ¥1199', // 更新内容
};
try {
floatingBallController?.updateFloatingBall(updateParams)
.then(() => {
console.info('闪控球内容已更新');
})
.catch((err: BusinessError) => {
console.error(`更新失败: ${err.code}, ${err.message}`);
});
} catch (error) {
console.error(`更新异常: ${error.code}, ${error.message}`);
}
重要限制:
- 更新时不能更改模板类型 (如从
EMPHATIC改为NORMAL),否则会抛出错误码1300027。 - 静态模板(
STATIC)不支持更新,调用updateFloatingBall会返回错误码1300028。 - 其他参数(标题、内容、背景色、图标)均可按需修改。
3.4 -> 停止闪控球:stopFloatingBall()
当闪控球不再需要时(如用户退出相关页面、抢单超时),应调用stopFloatingBall将其从屏幕上移除,并释放系统资源。
typescript
floatingBallController?.stopFloatingBall()
.then(() => {
console.info('闪控球已停止');
floatingBallController = undefined; // 清空引用
})
.catch((err: BusinessError) => {
console.error(`停止失败: ${err.code}, ${err.message}`);
});
4 -> 交互与生命周期管理:事件监听
闪控球并非"单向输出",开发者需要感知用户的点击行为以及闪控球自身的状态变化,从而完成业务闭环。
4.1 -> 监听生命周期状态:on('stateChange')
通过stateChange事件,可以获知闪控球何时启动、何时停止,这对于埋点统计或业务逻辑联动十分有用。
typescript
// 定义状态回调
const onStateChange = (state: floatingBall.FloatingBallState) => {
switch (state) {
case floatingBall.FloatingBallState.STARTED:
console.info('闪控球已启动,可在此上报曝光埋点');
break;
case floatingBall.FloatingBallState.STOPPED:
console.info('闪控球已停止,可清理相关资源');
break;
default:
break;
}
};
try {
floatingBallController?.on('stateChange', onStateChange);
} catch (error) {
console.error(`注册状态监听失败: ${error.code}, ${error.message}`);
}
// 不再使用时,取消监听
// floatingBallController?.off('stateChange', onStateChange);
4.2 -> 监听点击事件:on('click')
点击是闪控球最核心的用户交互。在点击回调中,开发者通常会执行拉起应用主窗口、跳转指定页面等操作。
typescript
const onClick = () => {
console.info('用户点击了闪控球');
// 此处可执行恢复主窗口等操作
};
try {
floatingBallController?.on('click', onClick);
} catch (error) {
console.error(`注册点击监听失败: ${error.code}, ${error.message}`);
}
4.3 -> 恢复主窗口:restoreMainWindow()
在点击闪控球后,最常见的需求是恢复应用主窗口并加载指定页面。restoreMainWindow方法正是为此设计,它接受一个Want参数,用于指定要恢复的Ability和页面路径。
typescript
import { Want } from '@kit.AbilityKit';
let want: Want = {
bundleName: 'com.example.myapplication',
abilityName: 'EntryAbility',
// 可选:传递参数或指定页面
parameters: {
targetPage: 'priceDetail',
productId: '123456'
}
};
// 注意:restoreMainWindow 必须在闪控球的点击回调中调用
const onBallClick = () => {
floatingBallController?.restoreMainWindow(want)
.then(() => {
console.info('主窗口已恢复,并跳转至指定页面');
})
.catch((err: BusinessError) => {
console.error(`恢复主窗口失败: ${err.code}, ${err.message}`);
});
};
floatingBallController?.on('click', onBallClick);
关键约束 :该方法仅支持在点击闪控球后调用 ,否则会返回错误码1300025(状态不支持)。这意味着它通常与click事件配合使用。
4.4 -> 获取窗口信息:getFloatingBallWindowInfo()
若需要获取闪控球的窗口ID等底层信息,可调用此方法,主要用于高级调试或特殊窗口管理场景。
typescript
floatingBallController?.getFloatingBallWindowInfo()
.then((info: floatingBall.FloatingBallWindowInfo) => {
console.info(`闪控球窗口ID: ${info.windowId}`);
})
.catch((err: BusinessError) => {
console.error(`获取窗口信息失败: ${err.code}, ${err.message}`);
});
5 -> 完整实战示例:比价助手闪控球
下面通过一个完整的比价助手场景,串联上述所有API。当用户在电商App中浏览商品时,本应用在后台静默比价,若发现更低价则通过闪控球提示用户,用户点击后拉起应用详情页。
typescript
// 1. 能力检测
if (!floatingBall.isFloatingBallEnabled()) {
console.warn('设备不支持闪控球,使用备选方案');
return;
}
// 2. 创建控制器
let ctx = getContext(this) as common.UIAbilityContext;
let config: floatingBall.FloatingBallConfiguration = { context: ctx };
let controller: floatingBall.FloatingBallController | undefined;
floatingBall.create(config).then((ctrl) => {
controller = ctrl;
// 3. 注册点击事件(先注册,避免遗漏)
controller.on('click', () => {
// 恢复主窗口并跳转到详情页
let want: Want = {
bundleName: ctx.abilityInfo.bundleName,
abilityName: 'EntryAbility',
parameters: { route: 'priceDetail', productId: 'currentId' }
};
controller.restoreMainWindow(want).catch(console.error);
});
// 4. 注册状态变化事件
controller.on('stateChange', (state) => {
if (state === floatingBall.FloatingBallState.STOPPED) {
console.info('闪控球已停止,可清理状态');
}
});
// 5. 启动闪控球(初始状态)
let startParams: floatingBall.FloatingBallParams = {
template: floatingBall.FloatingBallTemplate.EMPHATIC,
title: '比价助手',
content: '正在比价中...',
backgroundColor: '#FF333333'
};
controller.startFloatingBall(startParams).catch(console.error);
}).catch(console.error);
// 6. 模拟比价完成后更新闪控球
setTimeout(() => {
if (controller) {
let updateParams: floatingBall.FloatingBallParams = {
template: floatingBall.FloatingBallTemplate.EMPHATIC,
title: '比价助手',
content: '发现更低价格 ¥899',
backgroundColor: '#FFE64545' // 醒目红色
};
controller.updateFloatingBall(updateParams).catch(console.error);
}
}, 5000);
// 7. 页面销毁时停止闪控球
onDestroy() {
controller?.stopFloatingBall();
controller?.off('click'); // 移除监听,避免内存泄漏
controller?.off('stateChange');
}
6 -> 总结与展望
闪控球作为鸿蒙6.0在窗口管理领域的一次重要创新,为开发者提供了一种轻量、高效、系统级的信息触达与交互方式。从技术特性来看,它兼具悬浮窗的"置顶性"与通知的"及时性",但又避免了悬浮窗的"侵入感"和通知的"易忽略性",在比价、搜题、抢单、即时通讯等场景中拥有广阔的应用前景。
在实际开发中,以下几点值得特别关注:
- 设备适配 :务必使用
isFloatingBallEnabled进行能力检测,并做好降级方案,保障用户体验一致性。 - 权限管理 :
ohos.permission.USE_FLOAT_BALL属于敏感权限,需合理说明用途并动态申请,避免审核驳回。 - 资源释放 :闪控球涉及窗口资源,在页面或Ability销毁时务必调用
stopFloatingBall并取消事件监听,防止内存泄漏。 - 交互设计:闪控球的内容应高度凝练(标题+简短内容),点击后的跳转逻辑要直接符合用户预期,避免冗长的加载过程。
- 模板选择 :根据场景选择合适的模板。例如,
SIMPLE适合仅展示标题的极简场景;EMPHATIC适合需要突出图标与内容的营销类信息。
未来,随着鸿蒙生态的不断演进,闪控球能力有望进一步扩展------例如支持更丰富的自定义布局、动态交互手势、多设备协同下的流转等。作为开发者,深入理解并善用这一功能,将能为用户创造出更流畅、更智能的轻量级交互体验。
感谢各位大佬支持!!!
互三啦!!!