WebToApp 悬浮窗(Floating Window)模式完全指南:配置项详解、系统权限与源码实现原理
2026/9/17 11:05:17 网站建设 项目流程

WebToApp 悬浮窗(Floating Window)模式完全指南:配置项详解、系统权限与源码实现原理

【免费下载链接】web-to-appThe most full featured web-to-app toolkit on Android, a complete APK workshop that runs entirely on your phone项目地址: https://gitcode.com/GitHub_Trending/web/web-to-app

本篇指南围绕 WebToApp 项目中 floating-window.md 所定义的「悬浮窗模式」展开,讲解如何在Edit Common Config编辑器中通过Floating window卡片,把一个 Web 应用以可自由缩放、可最小化、可吸附屏幕边缘的悬浮窗形态运行在其他应用之上。读完本文,你将掌握floatingWindowConfig的全部配置字段、各枚举取值(宽高比与边框样式)、系统悬浮窗权限的授予流程,以及前端配置卡片、运行时服务(FloatingWindowService)与导出链路(ApkConfigJsonFactory)之间的完整调用关系。

功能概览与适用场景

悬浮窗模式的核心能力是:不退出当前 App,也能同时使用另一个 Web 应用。典型场景包括:

  • 看视频 / 直播的同时,用悬浮小窗继续浏览网页内容;
  • 聊天类、文档类 Web 应用以画中画(Picture-in-Picture)形态常驻屏幕角落;
  • 临时把一个网页最小化为可拖动的圆形按钮,需要时一键还原。

从 FloatingWindowService.kt 的实现看,悬浮窗本质是一个**前台服务(foreground service)**叠加的TYPE_APPLICATION_OVERLAY窗口,窗口内部承载一个完整配置过的 WebView(含下载桥、翻译桥、原生桥等能力)。因此它不仅适合纯网页,也能承载 PHP、Python、Go、Node.js、WordPress 等需要服务端运行时的应用类型。

配置入口:Edit Common Config 中的 Floating Window 卡片

在应用编辑器的Edit Common Config页中,找到Floating window卡片,打开顶部的启用开关即可进入悬浮窗模式配置。该卡片的 UI 由 FloatingWindowConfigCard.kt 实现,卡片内按Size(尺寸)→ Appearance(外观)→ Behavior(行为)→ Advanced(高级)四个区块组织所有可调项。

配置数据在内存中统一建模为FloatingWindowConfig(定义于 WebApp.kt),所有字段都有默认值,这意味着你可以只改其中一两个字段,其余行为保持默认。

配置项详解

Enable —— 总开关

对应floatingWindowConfig.enabled(默认false)。只有该字段为true时,导出/运行时才会以悬浮窗形态启动应用;关闭则走常规全屏 WebView 模式。

Size —— 窗口尺寸与宽高比

尺寸相关字段及其默认值如下:

字段默认值说明
windowSizePercent80统一尺寸百分比(宽高同步缩放)
widthPercent/heightPercent80/80独立宽 / 高百分比
lockAspectRatiofalse是否锁定宽高比
aspectRatioModeFREE宽高比模式(见下)
customAspectRatioWidth/customAspectRatioHeight16/9自定义宽高比(仅CUSTOM模式生效)

在 UI 卡片中,宽度滑条范围为30%~100%;仅当宽高比模式为FREE时才会显示独立的高度滑条。选择任意非FREE的宽高比模式时,卡片会自动把lockAspectRatio置为true

FloatingWindowAspectRatioMode枚举(WebApp.kt)支持以下取值:

枚举值含义
SCREEN跟随屏幕宽高比
FREE自由缩放(默认)
RATIO_16_916:9
RATIO_9_169:16(竖屏视频友好)
RATIO_4_34:3
SQUARE1:1
CUSTOM自定义宽:高(滑条范围 1~32)

文档强调:默认是自由缩放lockAspectRatio = falseFREE),拖拽窗口四角可以任意改变形状;一旦锁定宽高比,缩放时会被约束在该比例内。从源码看,这个逻辑由 FloatingWindowManager.kt 中的resolveAspectRatio()calculateBoundedAspectSize()实现:FREE返回null(不约束),其余模式返回对应比例值并在宽/高方向做有界换算。值得注意的一个细节是:SCREEN模式只有在lockAspectRatio = true时才按屏幕比例生效,否则会被降级为FREE处理。

窗口的实际最小尺寸在运行时也有硬约束:拖动缩放手柄时最小宽度为屏幕宽度的 25%、最小高度为屏幕高度的 20%,且不会超过屏幕边界(见createResizeHandle()clampWindowParamsToScreen())。

Appearance —— 外观

字段默认值取值范围说明
opacity10030~100(%)悬浮窗整体不透明度
cornerRadius160~32(dp)窗口圆角半径
borderStyleSUBTLE见下边框样式

FloatingBorderStyle枚举(WebApp.kt)共 4 档,其视觉差异在 FloatingWindowManager.kt 的createStyledBackground()中体现:

枚举值视觉效果
NONE无边框
SUBTLE2px 深色细边框(#333355),默认档
GLOW3px 亮蓝边框(#6666FF),视觉上带发光感
ACCENT3px 紫色强调边框(#8B5CF6

不透明度opacity会直接映射到WindowManager.LayoutParams.alphaopacity / 100f),属于窗口级透明,整窗(含 WebView 内容)都会随之变淡。

Title bar —— 标题栏

字段默认值说明
showTitleBartrue是否显示标题栏
autoHideTitleBarfalse是否自动隐藏标题栏(仅显示时生效)

标题栏高度固定为 40dp,布局模仿 macOS 风格:左侧是三个「红黄绿」圆形按钮(关闭 / 最小化 / 全屏切换),中间显示应用名(空则显示 "WebToApp"),右侧是返回与前进两个导航按钮。返回/前进按钮的可用态由updateNavigationButtons()根据webView.canGoBack()/canGoForward()实时刷新(不可用时置灰、透明度降到 0.35)。

开启autoHideTitleBar后,标题栏会在 3 秒无操作后上滑淡出(AUTO_HIDE_TITLE_DELAY_MS = 3000L),用户一旦触摸窗口任意位置即恢复显示。若关闭标题栏且未锁定位置,窗口顶部会出现一条 22dp 高的拖拽条(带指示符),保证依然可以拖动窗口。

Minimized —— 最小化行为

字段默认值说明
startMinimizedfalse启动时直接进入最小化状态
minimizedIconPathnull自定义最小化图标路径
minimizedIconSizePercent100最小化按钮尺寸百分比(50~100)
minimizedIconEdgeDockingfalse最小化图标是否吸附屏幕边缘

最小化按钮的基础尺寸为 56dp,乘以minimizedIconSizePercent / 100得到最终大小。图标支持三种来源(见loadMinimizedIconBitmap()):content://URI、绝对路径、assets/相对路径;未配置时使用默认的 🌐 图形。开启minimizedIconEdgeDocking后,按钮会以 180ms 的带过冲(Overshoot)动画滑向最近屏幕边缘,并只露出 50% 的宽度/高度MINI_BUTTON_EDGE_VISIBLE_RATIO = 0.5f),形成「贴边收窄」的常驻效果。点击按钮即可还原窗口,还原位置由最小化按钮位置反推居中计算。

Behavior —— 行为与高级选项

字段默认值说明
rememberPositiontrue记住窗口位置,下次启动恢复
edgeSnappingtrue拖动松手时吸附屏幕边缘
showResizeHandletrue右下角显示缩放手柄
lockPositionfalse锁定位置,禁止拖动
  • 记住位置:窗口坐标写入SharedPreferences(文件floating_window_prefs,键position_x/position_y),下次启动时优先恢复上次坐标,并做屏幕边界钳制;首次启动默认居中。
  • 边缘吸附:拖动松手后,若窗口任一边距屏幕边缘小于 60px(EDGE_SNAP_THRESHOLD_PX),会以 200ms 动画平滑吸附到对应边缘(performEdgeSnap())。
  • 缩放手柄:28dp 大小的手柄固定在窗口右下角,拖动时按当前宽高比模式实时更新LayoutParams
  • 锁定位置:开启后标题栏/拖拽条不再响应拖动手势(setupDragHandler只在!config.lockPosition时挂载)。

这些开关都集中在卡片底部「Advanced」折叠区,属于进阶调优项。

运行前提:系统悬浮窗权限

文档明确提示:悬浮窗运行需要系统悬浮窗(overlay)权限。这是 Android 的安全限制,不是应用内开关能绕过的。

权限的检查与请求封装在 FloatingWindowService.kt 的伴生对象中:

  • canDrawOverlays(context):API 23(Android 6.0)及以上通过Settings.canDrawOverlays()检查,以下直接放行;
  • requestOverlayPermission(context):跳转Settings.ACTION_MANAGE_OVERLAY_PERMISSION(带package:参数)引导用户手动授权。

在实际启动链中,ShellActivity.kt 会先检查权限:已授权则直接launchFloatingWindowAndFinish();未授权则发起请求并把启动动作挂起到pendingFloatingWindowLaunch,待用户授权返回后(onResume中再次检查)再补启动。窗口本身使用TYPE_APPLICATION_OVERLAY(Android 8.0+)或旧版TYPE_PHONE类型创建,并常驻一条低优先级前台通知(channelfloating_window_channel,可一键关闭悬浮窗)。

源码级实现:从配置到窗口的完整链路

1. 配置建模

所有配置字段收敛在 FloatingWindowConfig 这一个 data class 中,直接以 Gson 序列化传递。它既用于宿主 App 的编辑器,也用于导出 APK 后的运行时解析(JSON 键名与字段名一一对应)。

2. 运行时启动链路

悬浮窗由前台服务 FloatingWindowService.kt 驱动,通过 Intent Extra 传递配置:

ShellActivity.launchFloatingWindowAndFinish() → startForegroundService(FloatingWindowService, ACTION_SHOW) → 解析 EXTRA_CONFIG(JSON) → FloatingWindowConfig → 组装 WebView 配置器(翻译桥/下载桥/原生桥/扩展模块/伪装配置) → FloatingWindowManager.show(config, appName, url)

服务支持 4 种动作:ACTION_SHOW(显示)、ACTION_DISMISS(关闭并停服)、ACTION_MINIMIZEACTION_RESTORE,后两个供外部(如通知、辅助入口)控制窗口状态。服务以START_NOT_STICKY返回,销毁时会依次停止服务端运行时、释放 WebView、移除全部悬浮视图。

3. 服务端运行时应用类型

若目标应用属于PHP_APP/PYTHON_APP/GO_APP/NODEJS_APP/WORDPRESS,服务会先以about:blank显示空窗口,再通过ShellServerLauncher.resolveServerBackedTargetUrl()异步拉起对应服务端运行时,拿到真实 URL 后注入 WebView(needsServerStartup()+ 协程分支)。这意味着悬浮窗模式不是网页专属,而是完整复用了 WebToApp 的本地运行时体系。

4. 导出与持久化

导出 APK 时,ApkConfigJsonFactory.kt 会把FloatingWindowConfig全量字段写入floatingWindowConfigJSON 块;对应测试 ApkConfigJsonFactoryTest.kt 验证了aspectRatioMode = "RATIO_16_9"customAspectRatioWidth = 21customAspectRatioHeight = 9minimizedIconSizePercent = 70minimizedIconEdgeDocking = true等取值能无损往返。而 ShellWebViewConfig.kt 则展示了从 ShellConfig 反解出运行时FloatingWindowConfig的兼容性处理:枚举解析失败时回退到「锁比例则 SCREEN、否则 FREE」的兜底策略,边框解析失败回退SUBTLE

限制与注意事项

  • 权限是硬前提:未授予系统悬浮窗权限时窗口无法显示,授权入口在系统设置页,代码无法静默获取;
  • 前台服务限制:在 targetSdk 34+ 上,后台直接启动前台服务可能被系统拒绝(ShellActivity已捕获该异常并记录日志),实际使用建议从应用前台触发;
  • 焦点与输入法:窗口默认FLAG_NOT_FOCUSABLE(不抢焦点、不弹输入法),当页面内输入框获得焦点时,通过注入的IME_FOCUS_TRACKER_JS桥接动态切换焦点状态以唤起输入法,失焦后自动释放——这是悬浮窗场景下保证「悬浮但不打扰」的关键设计;
  • 部分 Web 能力受限:从FloatingWindowServiceWebViewCallbacks回调可看到,文件选择、地理定位、权限请求在悬浮窗中默认被拒绝/关闭,属于有意的精简策略;
  • 尺寸与比例钳制:窗口百分比在 30%~100% 之间,比例锁定后按宽/高方向有界换算,不会出现窗口越界或无限缩小。

小结

悬浮窗模式是 WebToApp 面向「多任务并行」场景的关键能力。通过 Edit Common Config 中的 Floating window 卡片,你可以像配置一个桌面级画中画窗口一样,精确控制尺寸、宽高比、外观、标题栏、最小化行为与吸附逻辑;底层则由FloatingWindowConfig → FloatingWindowService → FloatingWindowManager → WindowManager的四层链路承载,从编辑器配置到导出 APK 后的运行时表现保持完全一致。掌握本文的字段语义与权限流程,即可在自己的 Web 应用中一键启用可缩放、可最小化、可贴边的悬浮窗体验。

【免费下载链接】web-to-appThe most full featured web-to-app toolkit on Android, a complete APK workshop that runs entirely on your phone项目地址: https://gitcode.com/GitHub_Trending/web/web-to-app

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询