需求:在应用内显示一个悬浮按钮,可以跨页面显示,页面切换时不闪。
本以为这个需求很正常的,应该问题不大,因为不需要在桌面显示,不需要覆盖其他应用,仅仅只在自己应用内部显示,但是深入了解之后发现,问题没那么简单。
首先,找到如下几种方案:
方案一. TYPE_SYSTEM_ALERT类型
WindowManager windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams()
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
windowManager.addView(view, layoutParams);
需要权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
7.1.1以下不需要权限声明,在魅族、华为、小米等机型上默认隐藏,需要引导用户打开悬浮窗。
可以参考如下:
Android 悬浮窗权限各机型各系统适配大全
https://github.com/zhaozepeng/FloatWindowPermission
在Android8.0上的变更:
使用 SYSTEM_ALERT_WINDOW
权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:
* TYPE_PHONE
* TYPE_PRIORITY_PHONE
* TYPE_SYSTEM_ALERT
* TYPE_SYSTEM_OVERLAY
* TYPE_SYSTEM_ERROR
应用必须使用名为 TYPE_APPLICATION_OVERLAY
的新窗口类型
方案二. TYPE_TOAST类型
系统在添加 TYPE_TOAST 类型控件时默认不需要权限,从而可以绕过悬浮窗权限。并且可以在桌面和其他应用上悬浮显示。
WindowManager windowManager = (WindowManager) applicationContext.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;
windowManager.addView(view, layoutParams);
但是这种做法并不适配所有机型,比如我亲测过的小米3上直接没有显示,但是也没有异常反馈。这样的话,我们的应用并不可控。
并且在Nexus6P(Android8.1系统)下,可以显示悬浮窗,但是切换到新的Activity 3.5s(超时)后浮窗会自动消失。
Android4.4以下无法接受点击事件。
这种情况如果可用的话是非常符合我们的要求的,但是在部分机型上不可控,并且Android新版本也对这种情况做了限制,所以这种情况也不太推荐使用。
方案三. TYPE_APPLICATION_PANEL类型
这种类型的资料较少,但是是一种相对较好的方案。
WindowManager windowManager = (WindowManager) applicationContext.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
layoutParams.token = activity.getWindow().getDecorView().getWindowToken(); // 必须要
windowManager.addView(view, layoutParams);
这种类型是不需要申明权限,因为这个类型的窗口本身就是应用级别的。
这个类型的问题是,初始化时需要绑定activity的windowtoken,也就是说切换activity时需要重新绑定,这就会闪一下。
哈哈哈,其实这种方式跟在activity根部局添加view很相似,切换activity都会闪一下。
也有一种思路是,在根部局加viewEnFloatingView,切换页面时用共享元素来解决闪动的问题。这种方式的问题是Android5.0以下系统不太好弄,而且共享元素有个问题,在切换时会显示共享元素,也就是悬浮控件隐私的情况下切换页面,悬浮控件会闪现一下。具体的实现起来也是很麻烦的呢
方案四. 自定义Toast
自定义Toast是希望用toast的形式显示悬浮窗,以此绕过权限检测。但是这种类型其实也就是TYPE_TOAST类型,局限也是一样。
====================================
这个方案,https://github.com/yhaolpz/FloatWindow
整合了上面的方案一二四,但是在部分小米手机上还是有问题的,魅族的手机没有测试过,情况估计也不太乐观。
=================================
最终方案建议:
- 采用方案三(TYPE_APPLICATION_PANEL类型)保底,不存在兼容性问题,使用也简单;
- 在已知的兼容机型/ROM上 使用方案二(TYPE_TOAST类型)提升体验。
=================================
Andorid 应用内悬浮控件实践方案总结
突破小米悬浮窗权限控制--不需要权限的悬浮窗
Android 悬浮窗权限各机型各系统适配大全
Android无权限弹悬浮窗适配8.0
Android开发中悬浮窗被禁用,无权限开启悬浮窗的解决方案
Android Toast Overlay攻击:无需任何权限的“Cloak and Dagger”