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

悬浮框 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.WindowManager BadTokenException:Unabletoaddwindowandroid.view.ViewRootImpl B a d T o k e n E x c e p t i o n : U n a b l e t o a d d w i n d o w a n d r o i d . v i e w . V i e w R o o t I m p l W@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);
}

你可能感兴趣的:(Android笔记系列)