Android6.0 实现一个悬浮窗的功能,解决方案有两种:
一是如果你做的是系统应用开发,只要给apk签名,那么默认悬浮窗权限是给予的,显然这种情况不符合大多数开发者的要求;
二是在开启悬浮窗之前,引导用户去开启权限 : 权限开启的UI路径是 “ 通用 -- 应用管理 -- 更多 -- 配置应用 --- 在其他应用的上层显示 --- 选择你的APP -- 运行在其他应用的上层显示 ”;
做系统悬浮窗需要申请权限,6.0以上的 还需要动态申请下的,以下为动态权限的申请:
【步骤1】在AndroidManifest.xml中添加悬浮窗的权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
【步骤2】悬浮窗的权限申请
public static int OVERLAY_PERMISSION_REQ_CODE = 1234;
private void checkAndRequestPermissions() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !Settings.canDrawOverlays(this)){
Intent intent =
new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent,OVERLAY_PERMISSION_REQ_CODE);
}else {
//todo 正常逻辑
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, @org.jetbrains.annotations.Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == OVERLAY_PERMISSION_REQ_CODE){
if(!Settings.canDrawOverlays(this)){
ToastUtil.showShort("权限授予失败,无法开启悬浮窗");
}else {
ToastUtil.showShort("权限授予成功,开启悬浮窗");
//todo正常逻辑
}
}
}
方法checkAndRequestPermissions() 可引导用户开机悬浮窗权限;
但是,在申请完权限后仍然不行,运行时会出现"permission denied for window type 2003" 的权限拒绝的提示,这里主要是出现在了这个类型的设置上,上边代码是错误的,也就是TYPE_SYSTEM_ALERT因为这个被遗弃了,不赞成使用,进去后看到,让使用 TYPE_APPLICATION_OVERLAY;因此我们需要根据不同版本使用不同代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){//6.0
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}else {
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
代码如下:
private void createFloatView() {
wmParams = new WindowManager.LayoutParams();
//获取WindowManagerImpl.CompatModeWrapper
mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
//设置window type
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){//6.0+
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}else {
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
// wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
//设置图片格式,效果为背景透明
wmParams.format = PixelFormat.RGBA_8888;
//设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//调整悬浮窗显示的停靠位置为左侧置顶
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
// 以屏幕左上角为原点,设置x、y初始值
wmParams.x = 1180;
wmParams.y = ViewUtil.oriPxToTarPx(200);
//设置悬浮窗口长宽数据
wmParams.width = ViewUtil.oriPxToTarPx(100);
wmParams.height = ViewUtil.oriPxToTarPx(100);
LayoutInflater inflater = LayoutInflater.from(getApplication());
//获取浮动窗口视图所在布局
mFloatLayout = (FrameLayout) inflater.inflate(R.layout.service_suspend, null);
mFloatLayout.setElevation(10F);
//添加mFloatLayout
mWindowManager.addView(mFloatLayout, wmParams);
//浮动窗口按钮
mFloatView = (ImageView) mFloatLayout.findViewById(R.id.image_suspend_service);
mFloatLayout.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
//设置监听浮动窗口的触摸移动
mFloatView.setOnTouchListener(new View.OnTouchListener() {
int x = 0;
int y = 0;
private boolean isMoving = false;
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
int newX = (int) event.getRawX() - mFloatView.getMeasuredWidth() / 2;
int newY = (int) event.getRawY() - mFloatView.getMeasuredHeight() / 2;
switch (action) {
case MotionEvent.ACTION_DOWN:
isMoving = false;
x = (int) event.getRawX() - mFloatView.getMeasuredWidth() / 2;
y = (int) event.getRawY() - mFloatView.getMeasuredHeight() / 2 - getStatusBarHeight();
break;
case MotionEvent.ACTION_MOVE:
if (Math.sqrt((double) (newX - x) * (double) (newX - x) + (double) (newY - y) * (double) (newY - y)) > ViewUtil.oriPxToTarPx(36) || isMoving) {
isMoving = true;
wmParams.x = newX;
wmParams.y = newY;
mWindowManager.updateViewLayout(mFloatLayout, wmParams);
}
break;
case MotionEvent.ACTION_UP:
if (Math.sqrt((double) (newX - x) * (double) (newX - x) + (double) (newY - y) * (double) (newY - y)) <= ViewUtil.oriPxToTarPx(36)) {
isMoving = false;
//逻辑处理
}
break;
default:
break;
}
return true;
}
});
}
add:
当时用的设备是9.0的,没问题,可以运行,之后开发,7.0,6.0以下版本的终端上会崩溃,会报异常permission denied for window type 2038, 经过多个实验发现这个权限
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
只能在安卓版本7.1之上的版本才能有效,所以可以修改一下判断:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1){//7.1+
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}else {
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
以上,只是修改了一个版本的判断,然后就可以继续运行了;
以上仅供参考;