Android笔记系列--悬浮窗详细6.0 8.0兼容处理

本文介绍了不同Android版本中实现悬浮窗功能的兼容性处理方法,包括6.0及以下版本仅需在清单文件中声明权限,6.0以上版本需动态申请权限并设置窗口类型,8.0以上版本还需额外配置特定类型的参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

悬浮框 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星点点-

请我喝杯咖啡呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值