Android悬浮窗的实现悬浮窗的实现(易错点易错点)
0. 前言前言
现在很多应用都使用到悬浮窗,例如微信在视频的时候,点击Home键,视频小窗口仍然会在屏幕上显示。这个功能在很多情况下都非常有用。那么
今天我们就来实现一下Android悬浮窗,以及探索一下实现悬浮窗时的易错点。
1. 实现原理实现原理
1.1 悬浮窗插入接口
在实现悬浮窗之前,我们需要知道通过什么接口,能够将一个控件放入到屏幕中去。
Android的界面绘制,都是通过WindowMananger的服务来实现的。那么,既然要实现一个能够在自身应用以外的界面上的悬浮窗,我们就要利用
WindowManager来“做手脚”。
(frameworks/base/core/java/android/view/WindowMananger.java)
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
...
}
WindowManager实现了ViewManager接口,可以通过获取WINDOW_SERVICE系统服务得到。而ViewManager接口有addView方法,我们就是通过
这个方法将悬浮窗控件加入到屏幕中去。
1.2 权限设置及请求
悬浮窗需要在别的应用之上显示控件,很显然,这需要某些权限才可以。
在API Level >= 23的时候,需要在AndroidManefest.xml文件中声明权限SYSTEM_ALERT_WINDOW才能在其他应用上绘制控件。
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
除了这个权限外,我们还需要在系统设置里面对本应用进行设置悬浮窗权限。该权限在应用中需要启动
Settings.ACTION_MANAGE_OVERLAY_PERMISSION来让用户手动设置权限。
startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), REQUEST_CODE);
1.3 LayoutParam设置
WindowManager的addView方法有两个参数,一个是需要加入的控件对象,另一个参数是WindowManager.LayoutParam对象。
这里需要着重说明的是LayoutParam里的type变量。这个变量是用来指定窗口类型的。在设置这个变量时,需要注意一个坑,那就是需要对不同版本
的Android系统进行适配。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
在Android 8.0之前,悬浮窗口设置可以为TYPE_PHONE,这种类型是用于提供用户交互操作的非应用窗口。
而Android 8.0对系统和API行为做了修改,包括使用SYSTEM_ALERT_WINDOW权限的应用无法再使用一下窗口类型来在其他应用和窗口上方显示
提醒窗口:
– TYPE_PHONE
– TYPE_PRIORITY_PHONE
– TYPE_SYSTEM_ALERT
– TYPE_SYSTEM_OVERLAY
– TYPE_SYSTEM_ERROR
如果需要实现在其他应用和窗口上方显示提醒窗口,那么必须该为TYPE_APPLICATION_OVERLAY的新类型。
如果在Android 8.0以上版本仍然使用TYPE_PHONE类型的悬浮窗口,则会出现如下异常信息:
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@f8ec928 -- permission denied for window type 2002
2. 具体实现具体实现
下面来讲解一下悬浮窗的具体实现方式。
完整的源码地址:https://github.com/dongzhong/TestForFloatingWindow
为了让悬浮窗与Activity脱离,使其在应用处于后台时悬浮窗仍然可以正常运行,这里使用Service来启动悬浮窗并做为其背后逻辑支撑。
在启动服务之前,需要先判断一下当前是否允许开启悬浮窗。
(MainActivity.java)
public void startFloatingService(View view) {
...
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 0);
} else {
startService(new Intent(MainActivity.this, FloatingService.class));