更新记录

1.2.7(2026-09-23) 下载此版本

修复一些bug

1.2.6(2026-09-19) 下载此版本

ios打包报错

1.2.5(2026-09-18) 下载此版本

增加水波纹动效以及动态配置

查看更多

平台兼容性

uni-app(3.8.4)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ × × √ √ √ × -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(3.8.4)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × × × × ×

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

lj-floatingball 桌面悬浮球

Android 平台的应用桌面悬浮球(UTS 插件)。悬浮球覆盖在桌面和其他 App 之上,支持自由拖拽、松手贴边吸附、点击 / 长按事件回传、数字角标与红点角标,并支持自定义形状(圆形 / 圆角矩形 / 长方形 / 胶囊)、背景色、图标与文字内容。

当前仅支持 Android 端。iOS 端为占位空实现,调用会走 fail 回调并返回 9010001。

分发方式

本插件为免费插件,源码随 uni_modules 一并分发,可自由查看、修改与二次开发。

建议始终从插件根目录导入:

import { showFloatingBall } from '@/uni_modules/lj-floatingball'

免费插件支持云端打包、离线打包与「安心打包」,无额外限制。

目录结构

uni_modules/lj-floatingball
├── package.json
├── readme.md
├── changelog.md
└── utssdk
    ├── interface.uts                       对外类型与 API 声明
    ├── unierror.uts                        错误码与错误信息
    ├── app-android
    │   ├── index.uts                       Android 实现
    │   ├── AndroidManifest.xml             SYSTEM_ALERT_WINDOW 权限
    │   └── config.json                     minSdkVersion
    └── app-ios
        └── index.uts                       iOS 占位实现

接入准备

1. 权限

插件已在 utssdk/app-android/AndroidManifest.xml 中声明 android.permission.SYSTEM_ALERT_WINDOW。

如需在云打包时稳定生效,建议同时在项目根目录 manifest.json 的 App 权限配置中声明该权限。SYSTEM_ALERT_WINDOW 属于特殊权限,无法通过 uni.requestPermissions 弹窗申请,必须跳转系统设置页由用户手动开启,插件已封装好跳转逻辑。

2. 自定义基座

插件包含 AndroidManifest.xml、原生资源等原生层配置,必须制作自定义调试基座后才能真机运行,标准基座无法加载。

插件升级到新版本后,也需要重新制作自定义基座才能生效。

快速上手

import {
  showFloatingBall,
  hideFloatingBall,
  updateFloatingBallBadge,
  updateFloatingBallText,
  onFloatingBallClick,
  onFloatingBallLongPress,
  FloatingBallOptions,
  FloatingBallClickEvent
} from '@/uni_modules/lj-floatingball'

onFloatingBallClick((event: FloatingBallClickEvent) => {
  console.log('悬浮球被点击', event.x, event.y, event.width, event.height)
})

onFloatingBallLongPress((event: FloatingBallClickEvent) => {
  updateFloatingBallBadge(0)
})

const options = {
  width: 120,
  height: 56,
  radius: 28,
  backgroundColor: '#007AFF',
  text: '客服',
  badge: 3,
  edgeSnap: true
} as FloatingBallOptions

showFloatingBall(options)

注意:options 必须使用 as FloatingBallOptions 断言,否则会被推断为 UTSJSONObject,字段读取可能不符合预期。

形状与尺寸

形状由 width / height(决定长宽)+ radius(决定圆角) 两个维度组合而成。

参数 类型 单位 说明
width number dp 球宽,优先级高于 size。传负数(推荐 -1)表示与屏幕等宽
height number dp 球高,优先级高于 size
size number dp 正方形简写,等价于同时设置 width 与 height,默认 56
radius number dp 圆角半径。不传 = 圆形;0 = 直角;> 0 = 对应半径的圆角

常见的四种形态:

// 1. 圆形(默认)
showFloatingBall({ size: 56 } as FloatingBallOptions)

// 2. 正方形 + 圆角
showFloatingBall({ size: 56, radius: 16 } as FloatingBallOptions)

// 3. 长方形(直角)
showFloatingBall({ width: 120, height: 56, radius: 0 } as FloatingBallOptions)

// 4. 胶囊形(圆角取高度的一半)
showFloatingBall({ width: 120, height: 56, radius: 28 } as FloatingBallOptions)

// 5. 满屏宽(width 传 -1,注意同时关掉拖拽与贴边)
showFloatingBall({
  width: -1,
  height: 56,
  radius: 0,
  margin: 0,
  draggable: false,
  edgeSnap: false
} as FloatingBallOptions)

尺寸优先级:width / height > size > 默认值 56。

满屏尺寸:width 传负数(推荐 -1)时,宽度自动取屏幕宽度,无需自己算 dp。此时水平方向不可拖拽、贴边吸附失效,建议同时传 draggable: false 与 edgeSnap: false;由于球宽等于屏宽,radius 不传会变成椭圆,满屏条请显式传 radius: 0 或具体圆角值。height 传负数同理(等于屏幕高度),但会占满整屏、拦截大量触摸,慎用。

内容构成

悬浮球由四部分叠加而成,可任意组合:

部分 来源 说明
背景 backgroundColor / borderColor / borderWidth / radius 形状与底色
图标 icon 球内图标,不传则不显示任何图片(显示纯色球)
文字 text / textColor / textSize 居中显示的文字,可省略
角标 badge / badgeDot 等 右上角数字或红点,可省略

图标与文字可以同时存在(两者都是居中的,实际使用时建议二选一)。

// 纯色球 + 文字
showFloatingBall({
  width: 96,
  height: 96,
  radius: 48,
  backgroundColor: '#007AFF',
  text: '客服',
  textColor: '#FFFFFF',
  textSize: 18
} as FloatingBallOptions)

// 图片球 + 角标
showFloatingBall({
  size: 56,
  icon: '/static/ball.png',
  badge: 5
} as FloatingBallOptions)

透明底(不绘制底色,投影一并关闭),只留图标或文字:

showFloatingBall({
  width: 120,
  height: 56,
  radius: 28,
  backgroundColor: 'transparent',
  text: '客服',
  textColor: '#007AFF',
  textSize: 18
} as FloatingBallOptions)

需要投影时(默认不绘制):

showFloatingBall({
  width: 120,
  height: 56,
  radius: 28,
  backgroundColor: '#007AFF',
  shadow: true,
  text: '客服'
} as FloatingBallOptions)

常驻扩散波纹(类似「监听中」的光环),以球心为圆心向外扩散并循环:

showFloatingBall({
  size: 56,
  backgroundColor: '#007AFF',
  ripple: true,
  rippleColor: '#80007AFF',
  rippleSize: 96,
  rippleCount: 2,
  rippleDuration: 1800,
  rippleLineWidth: 3
} as FloatingBallOptions)

不传 rippleSize 时,波纹的最大直径默认只比球大 8dp(每边多 4dp),想让波纹扩散得更远需要显式调大。

波纹是圆形的,会以球心为圆心向外扩散,所以用在长方形 / 胶囊形球上时,上下方露出的波纹会比左右方多。

波纹由独立的透明悬浮窗绘制(FLAG_NOT_TOUCHABLE,触摸完全穿透,不影响点击与拖拽),会覆盖在桌面和其他应用之上,且是常驻循环动画,存在持续的性能与耗电开销,非必要请保持关闭。

角标的颜色、尺寸和文字都可以自定义:

showFloatingBall({
  width: 120,
  height: 56,
  radius: 28,
  backgroundColor: '#007AFF',
  text: '客服',
  badgeText: '新',
  badgeColor: '#07C160',
  badgeTextColor: '#FFFFFF',
  badgeTextSize: 12,
  badgeSize: 20
} as FloatingBallOptions)

参数说明

showFloatingBall(options)

显示悬浮球。已显示时重复调用会更新配置:图标、文字、角标、位置即时生效;宽高、圆角、背景色、边框的变化会自动应用。

字段 类型 默认值 说明
width number - 球宽,单位 dp,优先级高于 size
height number - 球高,单位 dp,优先级高于 size
size number 56 正方形尺寸,单位 dp
radius number -1(圆形) 圆角半径,单位 dp
icon string 空(纯色球) 球内图标。支持代码包路径(如 /static/ball.png)、沙盒绝对路径、file:// 协议路径、android_asset 路径。不支持网络图片,网络图请先用 uni.downloadFile 下载成本地文件再传入
text string 空 球内文字,为空时不显示文字
textColor string #FFFFFF 文字颜色
textSize number 20 文字大小,单位 dp
x number 屏幕右侧 初始横坐标,单位 dp,相对屏幕左上角
y number 屏高 62% 初始纵坐标,单位 dp
margin number 8 贴边吸附时距屏幕边缘的距离,单位 dp
draggable boolean true 是否允许拖拽
edgeSnap boolean true 松手后是否吸附到左右边缘
touchSlop number 6 判定为拖拽的滑动阈值,单位 dp
backgroundColor string #FFFFFF 球背景色。支持 #RRGGBB、#AARRGGBB 与颜色关键字(如 red、black),不支持 rgb() / rgba() 写法。传 transparent、空字符串或全透明色(如 #00000000)表示不绘制底色
borderColor string #E8E8E8 球边框颜色,格式同上
borderWidth number 0 球边框宽度,单位 dp,0 表示无边框
shadow boolean false 是否显示投影,默认不绘制。背景为透明时始终不绘制
ripple boolean false 是否显示常驻扩散波纹(类似「监听中」的光环),以球心为中心向外扩散并循环播放
rippleColor string 球背景色的半透明版 波纹颜色,颜色格式同 backgroundColor
rippleSize number 球最长边 + 8 波纹扩散的最大直径,单位 dp,默认只比球大一圈(每边多 4dp)
rippleLineWidth number 2 圆环描边宽度,单位 dp
rippleCount number 2 同时扩散的圆环数量,错开相位
rippleDuration number 1500 单圈扩散时长,单位毫秒
badge number 0 角标数字,0 表示不显示
badgeDot boolean false 是否只显示小红点
badgeText string 空 角标文字,非空时优先于 badge 数字显示(如 新、HOT)
badgeColor string #FF3B30 角标背景色,颜色格式同 backgroundColor,不一定是红色
badgeTextColor string #FFFFFF 角标文字颜色
badgeTextSize number 10 角标文字大小,单位 sp
badgeSize number 16 数字角标高度(同时也是最小宽度),单位 dp,圆角取高度一半
badgeDotSize number 10 红点角标直径,单位 dp
badgeMax number 99 角标数字上限,超过显示为 99+
haptic boolean true 点击时是否触发轻微震动反馈
longPress boolean 注册长按回调时为 true 是否启用长按事件
longPressDelay number 500 长按判定时长,单位毫秒
autoRequestPermission boolean true 无悬浮窗权限时是否自动跳转系统设置页
bringToFrontOnClick boolean false 点击悬浮球时是否把本应用切回前台(仅 Android)
success (res: FloatingBallShowResult) => void - 成功回调,返回 errMsg / x / y / width / height
fail (res: FloatingBallFail) => void - 失败回调
complete (res: any) => void - 完成回调

API

hideFloatingBall()

隐藏悬浮球。已注册的点击 / 长按回调会保留,下次 showFloatingBall 依然有效。

updateFloatingBallBadge(count)

设置数字角标。count <= 0 隐藏角标,超过 badgeMax 显示 99+。调用后自动切换到数字模式。

showFloatingBallBadgeDot(show)

切换红点角标。true 显示纯红点(无数字),false 隐藏。

updateFloatingBallIcon(icon)

运行时替换球内图标,参数规则同 options.icon。

  • 传空字符串会清空图标,显示纯色球;
  • 传入的路径无法解码时会保留当前图标(不会把球清空)。

该 API 只在悬浮球已显示时生效。

updateFloatingBallText(text)

运行时替换球内文字。传空字符串则隐藏文字。

该 API 只在悬浮球已显示时生效。

setFloatingBallPosition(position)

设置悬浮球位置。position 为 { x: number, y: number },单位 dp,越界会自动收敛到屏幕内。

getFloatingBallPosition()

返回 { x, y, width, height }(单位 dp)。

isFloatingBallShowing()

返回悬浮球当前是否显示。若悬浮窗权限已被用户撤销(在系统设置里关掉了「显示在其他应用上层」),会顺带回收窗口并把状态同步为未显示,返回 false。

isFloatingBallPermissionGranted()

返回是否已获得悬浮窗权限。

requestFloatingBallPermission()

已授权返回 true;未授权则跳转系统悬浮窗权限设置页并返回 false。建议在页面 onShow 中再次调用 isFloatingBallPermissionGranted() 确认用户是否授权成功。

onFloatingBallClick(callback) / offFloatingBallClick()

注册 / 注销点击回调。回调参数为 { x, y, width, height }(单位 dp,球的当前位置与尺寸)。

该 API 以 on 开头且只有一个回调参数,UTS 不会在首次触发后自动回收回调,可以持续点击。请不要把点击逻辑写在 showFloatingBall 的 options 里,推荐始终使用本 API 注册。

重复调用会替换上一次注册的回调(只保留最新一个)。回调保存在原生侧,不会随页面卸载自动清除,页面重复创建也不会叠加触发;注销请调用 offFloatingBallClick()。

点击拉回前台

传 bringToFrontOnClick: true 后,点击悬浮球会把本应用切回前台:

showFloatingBall({
  width: 200,
  height: 50,
  radius: 48,
  text: '客服',
  bringToFrontOnClick: true
} as FloatingBallOptions)

行为说明:

  • 应用在后台时,点击后切回前台并停留在用户离开时的页面,不会跳回首页
  • 应用已在前台时无副作用,不闪烁、不跳转
  • 点击回调中的逻辑不会被切前台打断
  • Android 10 及以上同样可用
  • 只有「点击」会触发,拖拽和长按都不会
  • 仅 Android 生效,iOS 无对应能力

onFloatingBallLongPress(callback) / offFloatingBallLongPress()

注册 / 注销长按回调,参数同点击回调。注册后会自动启用长按判定。

动态更新方式

想改什么 用哪个 API
图标 updateFloatingBallIcon(path) 或 showFloatingBall({ icon })
文字 updateFloatingBallText(text) 或 showFloatingBall({ text })
角标 updateFloatingBallBadge(n) / showFloatingBallBadgeDot(bool)
位置 setFloatingBallPosition({ x, y }) 或 showFloatingBall({ x, y })
宽高 / 圆角 / 背景色 / 边框 showFloatingBall({ ... })

宽高、圆角、背景色、边框的变化需要重新调用 showFloatingBall() 应用,不需要手动 hideFloatingBall(),并且会保持当前位置,不会跳回默认位置。

建议始终传入完整配置对象,避免未传的字段被重置成默认值:

const ballConfig = {
  width: 120,
  height: 56,
  radius: 28,
  backgroundColor: '#007AFF',
  text: '客服',
  badge: 3,
  badgeColor: '#FF3B30',
  edgeSnap: true,
  draggable: true
} as FloatingBallOptions

showFloatingBall(ballConfig)                                            // 首次创建
showFloatingBall({ ...ballConfig, width: 160 } as FloatingBallOptions)  // 只改宽度

返回码

errCode 说明
9010001 当前平台不支持(iOS 或非 App 平台)
9010002 未获得悬浮窗权限
9010003 悬浮球未显示
9010004 参数错误或上下文获取失败

常见问题

Q:为什么设置 backgroundColor 没效果?

如果同时传入了不透明的图标,背景色会被图标完全盖住。想要纯色球就不要传 icon;想要「彩色底 + 图标」,图标需要是带透明通道的 PNG。

Q:为什么角标看不见 / 被挡住?

请先确认已重新制作自定义调试基座——插件更新后基座必须重建,否则可能加载到旧版本。若重建后仍然异常,请联系插件作者。

Q:点击回调不触发?

按以下顺序排查:

  1. 确认已通过 onFloatingBallClick(cb) 注册回调,回调在 showFloatingBall 前后注册均可;
  2. 确认自定义调试基座已重新制作——插件更新不会热更新;
  3. 若自定义了 touchSlop,取值过小会把点击误判为拖拽(此时不会触发点击回调);
  4. 按压超过 longPressDelay(默认 500ms)会触发长按回调而非点击回调。

Q:颜色字符串怎么写?

只支持 #RRGGBB、#AARRGGBB 以及颜色关键字(如 red)。写成 rgba(0,0,0,0.5) 会导致运行时报错,请改用 #80000000 这种 8 位十六进制写法。backgroundColor 额外支持 transparent 与空字符串,等价于不绘制底色。

Q:不想要球的底色或投影怎么办?

投影默认就是关闭的(shadow 默认 false),不会出现灰色的方形投影;需要投影时传 shadow: true。传 backgroundColor: 'transparent'(或 ''、#00000000)可以去掉球的底色,此时投影也会一并关闭。两者都需要重新执行 showFloatingBall() 才会在已显示的球上生效。

Q:能用网络图片作为图标吗?

不能直接使用。请先用 uni.downloadFile 下载到本地,再把本地路径传给 icon。

Q:能渲染 HTML 或 uni-app 组件吗?

不能。悬浮球是系统级原生窗口,内容只能由原生 View 组成(图片、文字、形状、角标)。要做复杂 UI 请改用页面内的 movable-view 方案。

Q:应用被从最近任务划掉后悬浮球消失了?

这是 Android 的正常行为,进程被回收后悬浮球会一起消失。若需要更强的后台存活能力,需要额外接入前台服务。

最低版本要求

  • HBuilderX 4.25 及以上
  • Android 5.0(API 21)及以上;Android 6.0(API 23)及以上需要用户手动授予「显示在其他应用上层」权限

变更记录

1.2.0

  • 新增满屏尺寸:width / height 传负数(推荐 -1)时自动取屏幕尺寸,无需自行换算 dp
  • 新增点击拉回前台:bringToFrontOnClick: true 时,点击悬浮球会把本应用切回前台,并停留在用户离开时的页面
  • 修复:满屏尺寸在横竖屏切换后未正确跟随

1.1.0

  • 新增形状控制:width / height 支持长方形,radius 支持圆角(圆形 / 圆角矩形 / 直角矩形 / 胶囊)
  • 新增文字内容:text / textColor / textSize 可在球上显示文字,新增 updateFloatingBallText() 动态更新
  • 新增自动应用:width / height / radius / backgroundColor 变化时自动应用新配置,并保持当前位置
  • 调整:icon 为空时不再加载 App 图标,改为显示纯色球;图标路径解码失败时保留原图标而非清空
  • 调整:颜色只支持十六进制写法与颜色关键字,不再支持 rgb() / rgba()
  • 修复:角标显示异常;触摸监听偶发失效
  • 类型清理:FloatingBallClickEvent / FloatingBallShowResult 的 size 字段拆分为 width / height;FloatingBallPosition 移除多余的 size 字段;新增 FloatingBallPositionInfo

1.0.0

  • 首个版本:悬浮球显示 / 隐藏、拖拽、贴边吸附、点击与长按回调、数字与红点角标、图标与位置动态更新、悬浮窗权限封装

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android.permission.SYSTEM_ALERT_WINDOW(悬浮窗权限,用于在其他应用上层显示悬浮球)

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

本插件不采集任何数据,不涉及服务器地址

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT协议