Android开发如何实现类似三星计时器的可移动悬浮弹窗
方案选型结论
这种支持拖拽移动、浮在内容上层、不遮挡下层原有显示的悬浮窗(比如三星系统的计时器悬浮窗),既不是基于Activity也不是基于Fragment实现,核心是通过Android系统的WindowManager系统服务直接挂载自定义View实现。如果只需要在自己APP内部显示,不需要退到桌面、打开其他APP时也常驻,也可以直接往当前Activity的视图根节点叠加自定义View,不需要特殊权限,实现成本更低。
为什么不选Activity/Fragment
- 用Activity实现的话,必须配置透明主题,不仅有页面切换的生命周期开销,跨页面常驻时还要处理任务栈配置,很容易出现悬浮窗闪烁、下层页面失焦的问题,拖拽逻辑适配也麻烦
- Fragment必须依附于宿主Activity存在,根本没法脱离当前Activity的视图层级跨页面、跨APP显示,完全不适合这类悬浮场景
全局悬浮窗开发流程(对齐三星计时器跨APP显示效果)
- 声明并申请权限
先在AndroidManifest.xml里添加悬浮窗权限声明:
安卓6.0及以上版本这个权限需要用户手动授权,需要跳转系统对应的悬浮窗权限设置页引导用户开启,注意适配不同厂商ROM的权限页跳转路径。<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> - 编写悬浮窗布局
按照正常Android布局的写法,写好计时器对应的UI布局文件,包含计时文本、控制按钮等元素即可,没有特殊写法要求。 - 配置窗口参数
拿到WindowManager系统服务实例,初始化WindowManager.LayoutParams配置项:- 窗口类型:安卓8.0及以上用
TYPE_APPLICATION_OVERLAY,低版本系统做兼容用对应旧的窗口类型即可 - 像素格式选
TRANSLUCENT,实现窗口透明,保证下层内容正常透传显示 - Flags必须加
FLAG_NOT_FOCUSABLE,避免悬浮窗抢占下层的输入焦点,保证下层应用、页面可以正常操作;不要加FLAG_NOT_TOUCHABLE,不然悬浮窗上的按钮、拖拽手势都没法响应 - 初始位置设置:gravity选左上角对齐,给x、y设置初始偏移值确定弹窗默认出现的位置
- 宽高设为
WRAP_CONTENT,让窗口大小刚好包裹布局内容,不会多余遮挡下层区域
- 窗口类型:安卓8.0及以上用
- 挂载悬浮窗
调用windowManager.addView(你的自定义悬浮View实例, 上面配置好的布局参数),就能把悬浮窗叠加到所有屏幕内容的最上层,下层原有内容完全可见、可正常操作。 - 实现拖拽移动逻辑
给自定义悬浮View设置OnTouchListener,在ACTION_DOWN事件里记录手指按下的初始坐标、当前悬浮窗的x/y位置;在ACTION_MOVE事件里计算手指移动的偏移量,更新布局参数的x、y值,调用windowManager.updateViewLayout()实时刷新悬浮窗位置,就能实现自由拖拽移动的效果。 - 销毁逻辑
不需要显示悬浮窗的时候,调用windowManager.removeView(悬浮View实例)把视图从窗口层移除即可,注意要在业务逻辑结束、APP退出时及时移除,避免内存泄漏。
应用内悬浮窗简化方案
如果不需要跨APP显示,完全不用申请特殊权限:
- 拿到当前Activity的
DecorView(也就是当前页面的根视图),直接把自定义悬浮View add到根视图里 - 布局参数用
FrameLayout.LayoutParams,和全局悬浮窗一样配置触摸拖拽逻辑即可 - 在Activity销毁的时候把悬浮View从根视图移除,避免内存泄漏
内容的提问来源于stack exchange,提问作者auotive
相关产品推荐
相关产品推荐

