黑马程序员
1、解决高版本小米、魅族等手机悬浮窗权限报Android permission denied for window type 2002错误。
2、解决黑马程序员教学视频中悬浮窗在高版本安卓手机上不能移动的问题。
Android6.0以上使用WindownManager实现悬浮窗会出现 Android permission denied for window type 2002 错误信息,这个是因为在Android6.0以上我们需要去设置页面设置 允许显示在其他应用上层 才可以正常使用悬浮窗。
6.0以上即使我们在清单文件中注册了
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
在有的手机中依然会报错 解决方法如下
首先判断权限不能用 ContextCompat.checkSelfPermission(context, permission)而是用下面的方法:
@RequiresApi(api = Build.VERSION_CODES.M)
public static boolean canShowAlert(Context context){
return Settings.canDrawOverlays(context);
}
如果此方法返回false说明没有开启在其他应用上层显示 这时我们需要让用户手动去开启 否则无法使用悬浮窗
Toast.makeText(getApplicationContext(),"请给予悬浮窗权限",Toast.LENGTH_SHORT).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, START_WINDOW);
解决黑马程序员教学视频中悬浮窗在高版本安卓手机上不能移动的问题
设置params.type 为WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY即可解决不能移动的问题,具体flags的含义可以参考
Android 悬浮窗基本使用
public void showToast() {
if (PermissionsUtil.canShowAlert(getApplicationContext())) {
Log.d(TAG, "showToast: --------------------------有权限可以弹窗");
mToast_view = View.inflate(getApplicationContext(), R.layout.toast_view, null);
//设置Toast的具体属性值
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
// | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
//得把原点定在左上才能保证坐标正确 应该是默认的原点不是左上角
params.gravity = Gravity.LEFT + Gravity.TOP;
//加载Toast的位置信息
int toast_location_x = SpUtil.getInt(this, SpKey.TOAST_LOCATION_X, 0);
int toast_location_y = SpUtil.getInt(this, SpKey.TOAST_LOCATION_Y, 0);
//控件左上角的x坐标
params.x = toast_location_x;
//控件左上角的y坐标
params.y = toast_location_y;
Log.d(TAG, "showToast: -----------------------------------params.x = " + params.x);
Log.d(TAG, "showToast: -----------------------------------params.y = " + params.y);
//如果不设置这个 悬浮窗不会被移动
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
mToast_view.setOnTouchListener(new View.OnTouchListener() {
private int mStartY;
private int mStartX;
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
//当点击时
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouch: ----------------------------悬浮窗被按下");
//记录起始位置
// getX是以组件左上角为坐标原点,获取X坐标轴上的值。
// getRawX是以屏幕左上角为左脚做预案,获取X坐标轴上的值。
mStartX = (int) event.getRawX();
mStartY = (int) event.getRawY();
break;
//当移动时
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onTouch: ----------------------------悬浮窗被移动");
//记录移动完的位置
int endX = (int) event.getRawX();
int endY = (int) event.getRawY();
//计算差值
int disX = endX - mStartX;
int disY = endY - mStartY;
//容错处理
//得到移动完的具体位置
params.x = params.x + disX;
params.y = params.y + disY;
//更新位置
mWm.updateViewLayout(mToast_view,params);
//重置起始位置 将最后的位置当成起始位置
mStartX = endX;
mStartY = endY;
break;
//当抬起时
case MotionEvent.ACTION_UP:
//保存位置
SpUtil.putInt(getApplicationContext(), SpKey.TOAST_LOCATION_X,params.x);
SpUtil.putInt(getApplicationContext(), SpKey.TOAST_LOCATION_Y,params.y);
break;
}
//只有触摸事件时返回值是true才能响应触摸事件
//既有触摸事件又有点击事件时 返回false 点击事件才能生效 P964
return true;
}
});
//给窗体添加这个吐司
mWm.addView(mToast_view, params);
}else {
Toast.makeText(this,"悬浮窗权限已被拒绝",Toast.LENGTH_SHORT).show();
}
}