悬浮框 6.0 8.0兼容处理
各版本的差别
6.0以下开悬浮窗只需在清单文件中申请权限
6.0以上需要动态权限申请,申请完权限设置type为TYPE_PHONE或TYPE_SYSTEM_ALERT就可以了
8.0需要添加的权限
在Android O之前的系统中申请了该权限后,再给对应的window设置
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
// 如果设置为TYPE_PHONE; 那么优先级会降低一些,即拉下通知栏不可见
// params.type = WindowManager.LayoutParams.TYPE_PHONE;
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
悬浮窗口就可以显示出来。
但是在Android O的系统中,google规定申请
android.permission.SYSTEM_ALERT_WINDOW
权限的应用需要给悬浮窗口设置如下type:
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
如果不设置该TYPE,应用会Crash,报错如下(后面的2002表示设置的type为TYPE_PHONE):
AndroidRuntime: android.view.WindowManagerBadTokenException:Unabletoaddwindowandroid.view.ViewRootImplBadTokenException:Unabletoaddwindowandroid.view.ViewRootImplW@c8d1f1a – permission denied for window type 2002
动态权限申请 android.permission.SYSTEM_ALERT_WINDOW权限是比较特殊的,使用下面的方法:
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 100);
在 onActivityResult(int requestCode, int resultCode, Intent data) 回调函数中处理。
注意:
LocalWindowManger可通过
activity.getWindowManager()或 activity.getWindow().getWindowManager()获取。
CompatModeWrapper可通过
getSystemService(Context.WINDOW_SERVICE)
当我们通过LocalWindowManger添加视图时,退出Activity,添加的视图也会随之消失。每一个Activity对应一个LocalWindowManger,每一个App对应一个CompatModeWrapper),所以要实现在App所在进程中运行的悬浮窗口,当然是得要获取CompatModeWrapper,而不是LocalWindowManger。
完整设置悬浮框代码如下:
private void createFloatView() {
btn_floatView = new Button(getApplicationContext());
btn_floatView.setText("悬浮窗");
//获取LayoutParams对象
wmParams = new WindowManager.LayoutParams();
//获取的是LocalWindowManager对象
mWindowManager = getWindowManager();
Log.i(TAG, "mWindowManager1--->" + mWindowManager);
// mWindowManager = getWindow().getWindowManager();
// Log.i(TAG, "mWindowManager2--->" + mWindowManager);
// 获取的是CompatModeWrapper对象
// mWindowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
// Log.i(TAG, "mWindowManager3--->" + mWindowManager);
/**
* 设置window type
* 分两种情况:
* (1)显示在其它应用上面
* 8.0以上设置TYPE_APPLICATION_OVERLAY
* 8.0以下设置TYPE_SYSTEM_ALERT
*
* (2)显示在当前应用
* 设置TYPE_PHONE
*/
// 如果设置为TYPE_PHONE; 那么优先级会降低一些,即拉下通知栏不可见
// wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//8.0+,不设置这个flag可能会报错
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
// 设置图片格式,效果为背景透明
wmParams.format = PixelFormat.RGBA_8888;
// 设置Window flag
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// 下面的flags属性的效果形同“锁定”。 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。
// wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL| LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE;
//调整悬浮窗显示的停靠位置为左侧置顶
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
// 以屏幕左上角为原点,设置x、y初始值,相对于gravity
wmParams.x = 0;
wmParams.y = 0;
//设置悬浮窗口长宽数据
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
// 设置悬浮窗的Touch监听
btn_floatView.setOnTouchListener(new View.OnTouchListener() {
int lastX, lastY;
int paramX, paramY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
paramX = wmParams.x;
paramY = wmParams.y;
break;
case MotionEvent.ACTION_MOVE:
int dx = (int) event.getRawX() - lastX;
int dy = (int) event.getRawY() - lastY;
wmParams.x = paramX + dx;
wmParams.y = paramY + dy;
// 更新悬浮窗位置
mWindowManager.updateViewLayout(btn_floatView, wmParams);
break;
}
return true;
}
});
mWindowManager.addView(btn_floatView, wmParams);
}