【共创稿事节】HarmonyOS 7 闪控窗实战:标准悬浮窗、侧边栏暂存与闪控球一键切换

本文基于 HarmonyOS 7(API 26)官方《闪控窗开发指导》与《闪控球开发指导》及新能力一览整理。文中接口名、权限名与版本号均核验自官方文档并标注出处;代码为V哥按官方开发步骤自写的示例,真机表现以实际设备为准;涉及 HarmonyOS 7 的能力需升级至 HarmonyOS 7 并以实际支持机型为准。


引子:通知栏装不下的常驻状态

V哥先说一个所有应用都躲不开的尴尬:你的应用有一个需要一直挂在屏幕上的状态,该放哪?

下载进度是典型。放通知栏,用户下拉才能看,进度一变通知栏还叮咚乱响;放在应用页面里,用户切去刷视频,进度就看不见了;自己做一个悬浮窗,位置、拖拽、边界、收纳、跟其他应用的窗口打架,全得自己管,一套逻辑写下来比业务本身还厚。

通话小窗、秒表计时、盯盘行情、歌词显示,全都是同一类问题------V哥把它叫常驻状态。常驻状态有三个天生需求:看得见(切到别的应用也能看)、拖得动(别挡着用户手头的事)、收得起(暂时不看时别占地方)。

HarmonyOS 7(API 26)给出的答案是闪控窗 。官方新能力一览的原话是:"接入标准悬浮窗,常驻展示实时状态,支持自由拖动、侧边栏暂存,与闪控球一键切换,带来更高效的多任务体验。"(官方新能力一览)V哥把这句话翻译成大白话:官方把"拖不乱、收得起、看得见"这三件事,系统全包了。

这期V哥把闪控窗拆开讲:能力边界、约束清单、接入代码,最后聊V哥对"常驻状态该住哪"的判断。


一、闪控窗到底是什么:系统托管的标准化悬浮窗

先看官方定义(闪控窗开发指导):

闪控窗是悬浮在桌面或其他应用界面上的小型窗口,为应用提供灵活的窗口管理能力。应用可以在小窗口中展示内容或提供快捷操作,用户可以在进行其他界面操作的同时查看闪控窗内容,提升使用体验。

关键在后半段文档里的一句定性:闪控球和闪控窗均为一种特殊的应用辅助窗口,具备在应用主窗口和对应 UIAbility 退至后台后仍在前台显示的能力。这句话就是"常驻"二字的官方出处------你的 UIAbility 都退后台了,闪控窗还替你站在前台。

很多人会问:这不是悬浮窗吗?跟 SYSTEM_FLOAT_WINDOW 那套全局悬浮窗有什么区别?官方文档给了一张对比表,V哥摘重点:

对比项 全局悬浮窗 闪控窗
UI 绘制 开发者自己画,无统一 UI 与动效 系统统一绘制窗口外壳与动效
联动能力 支持与闪控球绑定、一键切换
设备支持 仅 2in1 Phone、Tablet、2in1

V哥认为这张表里最值钱的是第一行。自己做悬浮窗,最痛苦的不是画内容,是外壳:标题栏、拖拽热区、贴边吸附、收起展开,每个细节都得自己磨。闪控窗把这些全部系统化------应用只填内容,壳归系统。标题栏上的关闭、缩小按钮,拖到屏幕边缘收进侧边栏,收成一个闪控球,全是系统统一行为,体验跟整机一致。

官方描述的三大特性,逐条对应到文档里的真实机制:

  • 自由拖动 :用户拖拽闪控窗的拖动热区改变位置,拖拽时自动避让状态栏、导航条、输入法键盘等系统组件。避让逻辑系统做,应用一行代码不用写。
  • 侧边栏暂存 :在 Phone 和 Tablet(非自由多窗模式等场景)上,闪控窗可进入系统侧边栏暂存------未绑定闪控球时,点击标题栏最小化按钮即收起到侧边栏,随取随用。这是"收得起"的官方实现。
  • 与闪控球一键切换:闪控窗与闪控球绑定后,点击缩小按钮即可互切,窗是"更多信息、便捷操作",球是"少量关键信息、极速收纳"。

二、接入之前:把约束清单背下来

V哥的习惯是动手前先背约束,闪控窗的约束条条都硬(均出自闪控窗开发指导):

  1. 版本与模型 :API 26.0.0 起支持,仅 Stage 模型可用。
  2. 权限 :需申请 ohos.permission.FLOAT_VIEW,这是 user_grant 权限------不只声明,还要在运行时向用户申请授权;若要与闪控球绑定使用,需同时申请 ohos.permission.USE_FLOAT_BALL
  3. 启动时机仅允许应用在前台时启动闪控窗。想从后台静默拉起?没有这个口子。
  4. 单实例同一个应用只能启动一个闪控窗,重复启动返回错误码 1300033。此外,应用已启动闪控球或画中画窗口时,也无法启动闪控窗,需先停掉。
  5. 设备范围:Phone、Tablet、2in1;能力是否可用可用接口先行探测。
  6. 尺寸限制:窗口大小必须在系统给定的限制范围内,尺寸与宽高比限制通过接口获取,超范围设置会被系统自动调整回范围内。
  7. 模板形态 :目前支持圆角矩形(FloatViewTemplateType.ROUNDED_RECTANGLE)和水平条状矩形(FloatViewTemplateType.RHORIZONTAL_BAR)两类模板------前者适合常规内容,后者是横向细长条,遮挡小,适合盯盘、歌词这类"一条信息"的场景。

V哥特别提醒第 3 条和第 4 条。前台启动意味着闪控窗的入口必须设计在应用内------官方的思路很清楚:这是用户主动打开的"状态窗口",不是应用偷偷塞到用户屏幕上的"弹窗"。单实例则意味着别指望用闪控窗做多任务列表,一个应用一个,用完即收,克制是这个能力的设计基调。


三、动手:五步接入一个下载进度闪控窗

官方开发步骤是固定的六拍:探测能力 → 创建控制器 → 设内容 → 设尺寸 → 启动 → 按需停止。V哥按自己的下载进度示例走一遍(接口名以官方 SDK 文档为准)。

第一步:权限与能力探测

module.json5 里声明权限:

json5 复制代码
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.FLOAT_VIEW",     // 闪控窗基础权限,user_grant
        "reason": "$string:float_view_reason",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      }
    ]
  }
}

user_grant 权限要在用户点"开启悬浮进度"时动态申请;申请前先探测设备能力:

typescript 复制代码
import { floatView } from '@kit.ArkUI';

// V哥注:能力探测放最前面,不支持的设备直接走降级(通知栏/应用内进度条)
if (!floatView.isFloatViewEnabled()) {
  this.useInAppProgress(); // 降级路径
  return;
}

第二步:创建控制器并注册回调

typescript 复制代码
// V哥注:控制器创建后先挂回调,再配置内容------状态变化别漏听
let ctx = this.getUIContext().getHostContext() as common.UIAbilityContext;
const config: floatView.FloatViewConfiguration = {
  context: ctx,
  templateType: floatView.FloatViewTemplateType.ROUNDED_RECTANGLE // 下载进度用常规矩形
};
this.floatViewController = await floatView.create(config);

// 状态回调:启动完成、被用户关闭、收起到侧边栏,都在这里收到
this.floatViewController.onStateChange((info: floatView.FloatViewStateChangeInfo) => {
  if (info.state === floatView.FloatViewState.STOPPED) {
    // V哥注:窗口已停止,注销回调并释放控制器引用
    this.floatViewController.offStateChange();
    this.floatViewController = undefined;
  }
});

第三步与第四步:内容与尺寸

typescript 复制代码
// 设置闪控窗页面内容:ProgressPage 是V哥自己写的进度页组件
await this.floatViewController.setUIContext('pages/ProgressPage');

// 尺寸别拍脑袋:先问系统要限制范围,再在范围内设
const limits: floatView.FloatViewLimits =
  floatView.getFloatViewLimits(floatView.FloatViewTemplateType.ROUNDED_RECTANGLE);
const size: window.Size = { width: limits.maxSize.width, height: limits.maxSize.height / 2 };
await this.floatViewController.setWindowSize(size);

getFloatViewLimits() 这个设计V哥要夸:窗口尺寸的边界由系统说了算,应用先问再设 。超了会被系统悄悄拉回范围内,所以官方还配了 onRectChange()onLimitsChange() 两个回调,让应用能感知"实际窗口变成了多大"。

第五步与第六步:启动与停止

typescript 复制代码
await this.floatViewController.start(); // 启动后即可自由拖动、可最小化收进侧边栏

// 下载完成或用户取消时,V哥主动停掉窗口
await this.floatViewController.stop();

从启动那一刻起,拖动避让、侧边栏暂存、垃圾桶删除这些交互全是系统的,V哥的示例代码里没有一行在处理拖拽------这就是"接入标准悬浮窗"五个字的真实含义。


四、进阶:绑定闪控球,一个状态两种形态

单独接闪控窗已经够用,但官方给的最顺手的姿势是闪控窗 + 闪控球绑定闪控球开发指导)。绑定之后的行为规则,官方写得非常明确:

  • 绑定成功后,调用任一控制器的启动接口会同时创建闪控窗和闪控球,同一时刻仅展示其中一个
  • 用户点击缩小按钮触发互切:窗收成球,球展成窗,当前显示什么由用户点击决定
  • 调用任一控制器的停止接口,两个窗口同时销毁

落到代码上,差异只在多创建一个闪控球控制器、多调一次绑定:

typescript 复制代码
// V哥注:绑定场景需同时具备 FLOAT_VIEW 与 USE_FLOAT_BALL 两个权限
const ballConfig: floatingBall.FloatingBallConfiguration = {
  context: ctx,
  template: floatingBall.FloatingBallTemplate.EMPHATIC, // 强调文本:图标+标题+内容
  title: '下载中',
  content: 'package-25.3.1 68%'
};
const ballController = await floatingBall.create(ballConfig);

// 绑定后 start() 一次拉起两个,收起来只剩一颗球挂在屏幕边
await floatView.bind(this.floatViewController, ballController, ballConfig);
await this.floatViewController.start();

V哥给这个组合起了个名字:全景态与收纳态。全景态(闪控窗)负责"看得全"------进度条、速度、暂停按钮都摆得下;收纳态(闪控球)负责"不碍事"------用户去回消息、刷视频时收成一颗小球,关键信息(百分比)还在球上滚。两个状态之间的一键切换,把"收得起"做到了不需要用户学习。

有一个体验细节值得写进需求文档:绑定点切换时,闪控球不触发 click 回调------因为"展开成窗"本身就是切换动作,不是业务点击。业务自己的点击响应,留给未绑定的独立闪控球场景去处理。


五、V哥的判断:常驻状态不该塞进通知栏

写到这里,说点V哥自己的观点。

通知栏和常驻状态是两种东西,历史上我们一直把它们混着用。 通知是"事件"------发生了什么,看完可以划走;常驻状态是"过程"------正在进行,需要持续可见、持续可控。把过程塞进通知栏,等于把一个住客关进了候车厅:他没地方拖行李(不能自由挪),没地方休息(不能收纳),还老被人流挡住(下拉才可见)。闪控窗的价值,本质是给"过程"分了一套正经住房:看得见、拖不乱、收得起,三件事一件不缺。

第二点判断:窗口外壳的系统化,是比 API 更重要的红利。 过去每个做悬浮能力的应用都在重复造同一套壳:拖拽、贴边、避让、收纳,做十家是十种手感,没有一家跟系统一致。闪控窗把壳收归系统,应用回归"填内容"的本分------用户在任何一个应用里看到的拖动手感、收起动效都一样。V哥认为这种"一致性红利"会反过来抬升用户预期:7.0 之后,谁家的常驻状态还藏得深、收不干净,用户会觉得是应用的问题,而不是系统的限制。

第三点是提醒:克制使用。 前台启动、单实例、user_grant 授权,官方把每一道闸门都设成"用户主动"的形状。闪控窗适合下载、通话、计时、盯盘、歌词这类真正需要常驻的过程,不适合营销弹层------把通知的旧毛病搬进新窗口,等于把新房子又住成候车厅。

升级与机型提示照旧:闪控窗是 HarmonyOS 7(API 26)能力,需升级至 HarmonyOS 7,且以实际支持机型为准;不同设备上侧边栏暂存的生效条件(自由多窗模式、电脑模式等)以真机实测为准。


参考与出处

本文涉及的机制、接口与权限均来自以下官方文档:


最后一句:通知栏是候车厅,闪控窗是正经住房------把过程类状态从通知里接出来,让用户拖得动、收得起、看得见,常驻体验这才算有了着落。

相关推荐
贾伟康3 小时前
【HarmonyOS 7新能力|019】平行视界入门实战:从能力边界到最小可运行链路
harmonyos·arkts·arkui·harmonyos 7·平行视界
技术任我行XTing4 小时前
【DFX系列】Flutter 鸿蒙应用外接纹理介绍及问题定位
flutter·harmonyos
三翼鸟数字化技术团队4 小时前
WiFi-DensePose × OpenHarmony 智慧家居融合
harmonyos
HarmonyOS_SDK4 小时前
AI 赋能 Push Kit 场景化消息开发,高效完成鸿蒙应用推送能力接入
harmonyos
大雷神5 小时前
【共创稿事节】HarmonyOS ArkGraphics 3D实操——做一个可暂停、可拖动的音箱场景动画
harmonyos
anthonyzhu5 小时前
MacOS27 x86限制引发的pod的问题处理
华为·harmonyos
贾伟康5 小时前
【HarmonyOS 7新能力|030】沉浸光感工程封装:把接入逻辑放进可维护的分层结构
harmonyos·arkts·arkui·harmonyos 7·交互动效
万物智能信息科技5 小时前
GPIO控制状态灯—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
人工智能·华为·开源·harmonyos·鸿蒙
威哥爱编程6 小时前
HarmonyOS 7 平行视界 EasyGo 实战:配置文件接入应用内分屏,1:2/2:1 随意切
harmonyos·arkts