创建悬浮窗以及基于无障碍服务的窗口

  • 关于悬浮窗的创建
  • 启动悬浮窗
  • 关于前台服务
  • 启动服务
  • 无障碍窗口



知识点

参考链接

关于悬浮窗的创建

  1. 首先需要获取WindowManager
WindowManager manager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
  1. 为窗口添加参数(大小、位置、类型…)
WindowManager.LayoutParams viewParam = new WindowManager.LayoutParams();

然后可以设置窗口的Params,比如viewParams.type = WindowManager.LayoutParams.Type_APPLICATION_OVERLAY

  1. 创建一个View作为Window的布局和UI
View view = LayoutInflater.from(this).inflate(R.layout.view2, null);
  1. 将之前创建的View 和Params添加进入Window
manager.addView(view, viewParam);

通过以上几步可以创建一个悬浮窗。

注意点: 关于窗口的type不能乱设,有相关的要求,具体参考官方文档

记录一个坑点:需求是创建一个带有半透明图片的悬浮窗覆盖整个手机屏幕,点击和滑动事件要透过悬浮窗----z这个需求很明确,所以要想手势穿过这个悬浮窗,就需要在设置Params的时候设置Window的flags为viewParam.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;这里有个问题就是窗口类型如果是TYPE_APPLICATION_OVERLAY,在android R上可以生效,但是在android S上手势传不过悬浮窗,解决方法:

Window flag: this window can never receive touch events.
The intention of this flag is to leave the touch to be handled by some window below this window (in Z order).
Starting from Android Build.VERSION_CODES#S, for security reasons, touch events that pass through windows containing this flag (ie. are within the bounds of the window) will only be delivered to the touch-consuming window if one (or more) of the items below are true:
Same UID: This window belongs to the same UID that owns the touch-consuming window.
Trusted windows: This window is trusted. Trusted windows include (but are not limited to) accessibility windows (TYPE_ACCESSIBILITY_OVERLAY), the IME (TYPE_INPUT_METHOD) and assistant windows (TYPE_VOICE_INTERACTION). Windows of type TYPE_APPLICATION_OVERLAY are not trusted, see below.
Invisible windows: This window is View#GONE or View#INVISIBLE.
Fully transparent windows: This window has LayoutParams#alpha equal to 0.
One SAW window with enough transparency: This window is of type TYPE_APPLICATION_OVERLAY, has LayoutParams#alpha below or equal to the maximum obscuring opacity (see below) and it’s the only window of type TYPE_APPLICATION_OVERLAY from this UID in the touch path.
Multiple SAW windows with enough transparency: The multiple overlapping TYPE_APPLICATION_OVERLAY windows in the touch path from this UID have a combined obscuring opacity below or equal to the maximum obscuring opacity. See section Combined obscuring opacity below on how to compute this value.
If none of these cases hold, the touch will not be delivered and a message will be logged to logcat.

这里我是选择的降低透明度,降低到0.8一下

启动悬浮窗

悬浮窗要脱离activity而独立存在,所要选择使用Service来启动window,下面是整个项目关于Service的代码:

package com.example.picturedemo;

public class NotifyService extends Service {

    private WindowManager manager = null;
    private View view = null;
    private WindowManager.LayoutParams viewParam;
    private RemoteViews remoteViews;
    public ImageView picture;
    private static String model;
    private boolean flag = true;
    private boolean isRightPicture;
    private Bitmap bitmap;
    private int height;
    private int width;
    private int x;
    private NotificationManager notificationManager;
    private Notification notification;

    public NotifyService() {
    }

    @SuppressLint("ServiceCast")
    @RequiresApi(api = 31)
    @Override
    public void onCreate() {
        super.onCreate();
        NotifyBroadCastReceiver receiver = new NotifyBroadCastReceiver();
        model = Build.MODEL;
        Log.i("Version", model);
        IntentFilter filter = new IntentFilter();
        filter.addAction("PictureNotify");
        this.registerReceiver(receiver,filter);
        manager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
        viewParam = new WindowManager.LayoutParams();
        viewParam.x = WindowManager.LayoutParams.WRAP_CONTENT;
        viewParam.y = WindowManager.LayoutParams.WRAP_CONTENT;
        //windowInsetsController.hide(WindowInsets.Type.statusBars());
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) {
            viewParam.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            viewParam.type = WindowManager.LayoutParams.TYPE_PHONE;
            viewParam.type = WindowManager.LayoutParams.TYPE_STATUS_BAR;
        }
        viewParam.alpha = (float) 0.5;
        viewParam.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
                //WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS|
                //WindowManager.LayoutParams.FLAG_FULLSCREEN|
                //WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN|
                WindowManager.LayoutParams.TYPE_STATUS_BAR;

//        Display display = manager.getDefaultDisplay();
//        Point p = new Point();
//        display.getRealSize(p);
//        viewParam.width = p.x;
//        viewParam.height = p.y;
        //viewParam.flags = WindowManager.LayoutParams.TYPE_STATUS_BAR;
        viewParam.systemUiVisibility =  View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|
                                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
                                        View.SYSTEM_UI_FLAG_FULLSCREEN|
                                        View.SYSTEM_UI_FLAG_LAYOUT_STABLE|
                                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
        viewParam.format = PixelFormat.TRANSLUCENT;
        remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification);
    }
    @SuppressLint("WrongConstant")
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if(startId == 1) {
            notificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE);
            // tong知栏的服务以及广播
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationChannel notificationChannel = new NotificationChannel("1", getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT);
                notificationChannel.enableLights(true);
                notificationChannel.enableVibration(false);
                if (notificationManager != null) {
                    notificationManager.createNotificationChannel(notificationChannel);
                }
            }

            Intent intent1 = new Intent("PictureNotify");
            intent1.putExtra("notDisplay", 1);
            PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.button2, pendingIntent1); // 不显示



            Intent intent2 = new Intent("PictureNotify");
            intent2.putExtra("leftDisplay", 2);
            PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, 1, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.button, pendingIntent2); //  左显示


            Intent intent3 = new Intent("PictureNotify");
            intent3.putExtra("rightDisplay", 3);
            PendingIntent pendingIntent3 = PendingIntent.getBroadcast(this, 2, intent3, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.button3, pendingIntent3); // 右显示


            notification = new NotificationCompat.Builder(this, "1")
                    .setStyle(new NotificationCompat.DecoratedCustomViewStyle())
                    .setContent(remoteViews)
                    .setSmallIcon(R.mipmap.ic_launcher2)
                    .build();

            startForeground(1, notification);

            // 添加悬浮窗
            view = LayoutInflater.from(this).inflate(R.layout.view2, null);
            picture = view.findViewById(R.id.picture);

            if (manager != null) {
                 manager.addView(view, viewParam);
            }
            if (picture != null) {
                if (isL18()) {
                    ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right);
                    picture.setLayoutParams(params);
                    isRightPicture = true;
                    picture.setImageResource(R.drawable.l18right);
                } else {
                    ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneright);
                    picture.setLayoutParams(params);
                    isRightPicture = true;
                    picture.setImageResource(R.drawable.phoneright);
                }
                //picture.setAlpha((float)0.5);
            }
        }
        return super.onStartCommand(intent, flags, startId);

    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if(picture != null) {
            if (isL18()) {
                if(isRightPicture) {
                    ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right);
                    picture.setLayoutParams(params);
                    picture.setImageResource(R.drawable.l18right);
                } else {
                    ViewGroup.LayoutParams params = measurePicture(R.drawable.l18left);
                    picture.setLayoutParams(params);
                    picture.setImageResource(R.drawable.l18left);
                    isRightPicture = false;
                }

            } else {
                if(isRightPicture) {
                    ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneright);
                    picture.setLayoutParams(params);
                    picture.setImageResource(R.drawable.phoneright);
                } else {
                    ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneleft);
                    picture.setLayoutParams(params);
                    picture.setImageResource(R.drawable.phoneleft);
                    isRightPicture = false;
                }

            }
            //picture.setAlpha((float)0.5);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private ViewGroup.LayoutParams measurePicture(int drawable) {
        bitmap = BitmapFactory.decodeResource(getResources(), drawable);
        height= bitmap.getHeight();
        width= bitmap.getWidth();
        x = NotifyService.this.getResources().getDisplayMetrics().widthPixels;
        float ratio=(float) height/(float) width;
        ViewGroup.LayoutParams params = picture.getLayoutParams();
        params.width = x;
        params.height = (int) (ratio * x);
        return params;
    }

    private boolean isL18() {
        DisplayMetrics dm = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(dm);
        int width = dm.widthPixels;// 屏幕宽度(像素)
        //int height = dm.heightPixels;
        float density = dm.density;//屏幕密度(0.75 / 1.0 / 1.5)
        //int densityDpi = dm.densityDpi;//屏幕密度dpi(120 / 160 / 240)
        //屏幕宽度算法:屏幕宽度(像素)/屏幕密度
        int screenWidth = (int) (width/density);//屏幕宽度(dp)
        Log.i("ScreenWidth" , String.valueOf(width));
        if(width > 1440) {
            return true;
        }
        return false;
    }

    class NotifyBroadCastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if("PictureNotify".equals(action)) {
                int code1 = intent.getIntExtra("notDisplay" ,4);
                int code2 = intent.getIntExtra("leftDisplay",4);
                int code3 = intent.getIntExtra("rightDisplay" , 4);
                if(code1 == 1) {
                    //picture.setVisibility(View.GONE);
                    if(flag) {
                        manager.removeView(view);
                        remoteViews.setTextViewText(R.id.button2 , "显示");
                        notificationManager.notify(1,notification);

                        flag = false;
                    } else {
                        manager.addView(view , viewParam);
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        flag = true;
                    }


                    Log.i("CODE1" , String.valueOf(code1));
                } else if(code2 == 2) {
                    if(flag) {
//                        manager.removeView(view);
//                        flag = false;
                    } else {
                        manager.addView(view , viewParam);
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        flag = true;
                    }

                    Log.i("CODE2" , String.valueOf(code2));
                    if(isL18()) {
                        ViewGroup.LayoutParams params = measurePicture(R.drawable.l18left);
                        picture.setLayoutParams(params);
                        picture.setImageResource(R.drawable.l18left);
                    } else {
                        picture.setImageResource(R.drawable.phoneleft);
                    }
                    isRightPicture = false;
                } else if(code3 == 3) {
                    //picture.setVisibility(View.VISIBLE);
                    //picture.setAlpha((float) 0.5);
                    if(flag) {
                        //manager.removeView(view);
                        //flag = false;
                    } else {
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        manager.addView(view , viewParam);
                        flag = true;
                    }
                    Log.i("CODE3" , String.valueOf(code3));
                    if (isL18()) {
                        ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right);
                        picture.setLayoutParams(params);
                        picture.setImageResource(R.drawable.l18right);
                    } else {
                        picture.setImageResource(R.drawable.phoneright);
                    }
                    isRightPicture = true;
                }
            }
        }
    }
}

主要工作是三个:

  1. 创建窗口并添加到WindowManager
  2. 根据不同手机尺寸加载不同图片
  3. 开启前台服务去控制图片的显示与样式

关于前台服务

上一篇博客中有提到自定义通知栏,这里步骤差不多

  1. 自定义用于在通知栏显示的前台服务,步骤和创建通知栏一样,需要RemoteViews
// 创建通知渠道
notificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE);
            // tong知栏的服务以及广播
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationChannel notificationChannel = new NotificationChannel("1", getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT);
                notificationChannel.enableLights(true);
                notificationChannel.enableVibration(false);
                if (notificationManager != null) {
                    notificationManager.createNotificationChannel(notificationChannel);
                }
            }
remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification);
notification = new NotificationCompat.Builder(this, "1")
                    .setStyle(new NotificationCompat.DecoratedCustomViewStyle())
                    .setContent(remoteViews)
                    .setSmallIcon(R.mipmap.ic_launcher2)
                    .build();
  1. 启动前台服务
startForeground(1, notification);
  1. 要求前台服务(比如通知栏里面的按钮)可以得到响应,就需要广播了,在notification里面是由Pendinntent发出广播(并不是,具体发送代码在底层),这里是上层调用可以访问的接口
Intent intent1 = new Intent("PictureNotify");
            intent1.putExtra("notDisplay", 1);
            PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.button2, pendingIntent1); // 不显示
  1. 创建Receiver去接受广播事件
class NotifyBroadCastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if("PictureNotify".equals(action)) {
                int code1 = intent.getIntExtra("notDisplay" ,4);
                int code2 = intent.getIntExtra("leftDisplay",4);
                int code3 = intent.getIntExtra("rightDisplay" , 4);
                if(code1 == 1) {
                    //picture.setVisibility(View.GONE);
                    if(flag) {
                        manager.removeView(view);
                        remoteViews.setTextViewText(R.id.button2 , "显示");
                        notificationManager.notify(1,notification);

                        flag = false;
                    } else {
                        manager.addView(view , viewParam);
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        flag = true;
                    }


                    Log.i("CODE1" , String.valueOf(code1));
                } else if(code2 == 2) {
                    if(flag) {
//                        manager.removeView(view);
//                        flag = false;
                    } else {
                        manager.addView(view , viewParam);
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        flag = true;
                    }

                    Log.i("CODE2" , String.valueOf(code2));
                    if(isL18()) {
                        ViewGroup.LayoutParams params = measurePicture(R.drawable.l18left);
                        picture.setLayoutParams(params);
                        picture.setImageResource(R.drawable.l18left);
                    } else {
                        picture.setImageResource(R.drawable.phoneleft);
                    }
                    isRightPicture = false;
                } else if(code3 == 3) {
                    //picture.setVisibility(View.VISIBLE);
                    //picture.setAlpha((float) 0.5);
                    if(flag) {
                        //manager.removeView(view);
                        //flag = false;
                    } else {
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        manager.addView(view , viewParam);
                        flag = true;
                    }
                    Log.i("CODE3" , String.valueOf(code3));
                    if (isL18()) {
                        ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right);
                        picture.setLayoutParams(params);
                        picture.setImageResource(R.drawable.l18right);
                    } else {
                        picture.setImageResource(R.drawable.phoneright);
                    }
                    isRightPicture = true;
                }
            }
        }
    }
  1. 注册广播接收者
NotifyBroadCastReceiver receiver = new NotifyBroadCastReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("PictureNotify");
        this.registerReceiver(receiver,filter);

到此为止就可以响应前台服务的点击了,这里有个额外的点要注意:
要更新前台服务的通知栏里面的UI或者样式,除了直接使用remoteViews.setTextViewText(R.id.button2 , "显示");外还需要调用一次notificationManager.notify(1,notification);

启动服务

public class MainActivity extends AppCompatActivity {
    private static String model;
    private Button button;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent2 = new Intent(MainActivity.this, NotifyService.class);

                if(Settings.canDrawOverlays(MainActivity.this)) {

                        startService(intent2);
                } else {
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
                    startActivity(intent);
                }
                finish();

    }
}

需要配置权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

无障碍窗口

需求背景:上面的悬浮窗在设置的某些界面和下拉的通知栏不能覆盖

经过不懈的努力终于找到了解决办法

使用另外一个类型TYPE_ACCESSIBILITY_OVERLAY就可以解决此问题

但是当你开开心心使用的时候你会发现以下错误

Android 申请悬浮窗权限 android创建悬浮窗_android

经过查证一些资料之后发现这个类型必须和无障碍 AccessibilityService 搭配使用

下面是使用AccessibilityService后的代码:

public class NotifyService extends AccessibilityService implements LifecycleOwner {

    private WindowManager manager = null;
    private View view = null;
    private WindowManager.LayoutParams viewParam;
    private RemoteViews remoteViews;
    private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

    public ImageView picture;
    private static String model;
    private boolean flag = true;
    private Bitmap bitmap;
    private int height;
    private int width;
    private int x;
    private NotificationManager notificationManager;
    private Notification notification;

    public NotifyService() {
    }

    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }

    @SuppressLint("ServiceCast")
    @RequiresApi(api = 31)
    @Override
    public void onCreate() {
        super.onCreate();
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
        initObserve();
        openNotify();
    }

    private void initObserve() {
        ViewModleMain.INSTANCE.isShowWindow().observe(this, aBoolean -> {
            if(aBoolean) {
                showWindow();
            } else {
                if(!isNull(view)) {
                    if(view.getWindowToken() != null) {
                        if(manager != null) {
                            manager.removeView(view);
                        }
                    }
                }
            }
        });
    }

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

    }

    @Override
    public void onInterrupt() {

    }

    private void openNotify() {

        NotifyBroadCastReceiver receiver = new NotifyBroadCastReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("PictureNotify");
        this.registerReceiver(receiver,filter);
        remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification);
        // tong知栏的服务以及广播
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel notificationChannel = new NotificationChannel("1", getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT);
            notificationChannel.enableLights(true);
            notificationChannel.enableVibration(false);
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(notificationChannel);
            }
        }

        Intent intent1 = new Intent("PictureNotify");
        intent1.putExtra("notDisplay", 1);
        PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteViews.setOnClickPendingIntent(R.id.button2, pendingIntent1); // 不显示


        Intent intent2 = new Intent("PictureNotify");
        intent2.putExtra("leftDisplay", 2);
        PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, 1, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteViews.setOnClickPendingIntent(R.id.button, pendingIntent2); //  左显示


        Intent intent3 = new Intent("PictureNotify");
        intent3.putExtra("rightDisplay", 3);
        PendingIntent pendingIntent3 = PendingIntent.getBroadcast(this, 2, intent3, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteViews.setOnClickPendingIntent(R.id.button3, pendingIntent3); // 右显示


        notification = new NotificationCompat.Builder(this, "1")
                .setStyle(new NotificationCompat.DecoratedCustomViewStyle())
                .setContent(remoteViews)
                .setSmallIcon(R.mipmap.ic_launcher2)
                .build();
        notificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE);
        startForeground(1,notification);
    }

    private void showWindow() {
        manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(displayMetrics);
        viewParam = new WindowManager.LayoutParams();
        viewParam.x = WindowManager.LayoutParams.WRAP_CONTENT;
        viewParam.y = WindowManager.LayoutParams.WRAP_CONTENT;
        //windowInsetsController.hide(WindowInsets.Type.statusBars());
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) {
            viewParam.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                viewParam.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
            }
        } else {
            viewParam.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        }
        viewParam.alpha = (float) 0.5;
        viewParam.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
                //WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS|
                //WindowManager.LayoutParams.FLAG_FULLSCREEN|
                //WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN|
                WindowManager.LayoutParams.TYPE_STATUS_BAR;

//        Display display = manager.getDefaultDisplay();
//        Point p = new Point();
//        display.getRealSize(p);
//        viewParam.width = p.x;
//        viewParam.height = p.y;
        //viewParam.flags = WindowManager.LayoutParams.TYPE_STATUS_BAR;
        viewParam.systemUiVisibility =  View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|
                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
                View.SYSTEM_UI_FLAG_FULLSCREEN|
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE|
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
        viewParam.format = PixelFormat.TRANSPARENT;

        // 添加悬浮窗
        view = LayoutInflater.from(this).inflate(R.layout.view2, null);
        picture = view.findViewById(R.id.picture);

        manager.addView(view, viewParam);

        if (picture != null) {
            if (isL18()) {
                ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right);
                picture.setLayoutParams(params);
                picture.setImageResource(R.drawable.l18right);
            } else {
                ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneright);
                picture.setLayoutParams(params);
                picture.setImageResource(R.drawable.phoneright);
            }
            //picture.setAlpha((float)0.5);
        }
        //startForeground(1, notification);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if(picture != null) {
            if (isL18()) {
                ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right);
                picture.setLayoutParams(params);
                picture.setImageResource(R.drawable.l18right);
            } else {
                ViewGroup.LayoutParams params = measurePicture(R.drawable.phoneright);
                picture.setLayoutParams(params);
                picture.setImageResource(R.drawable.phoneright);
            }
            //picture.setAlpha((float)0.5);
        }
    }

    private ViewGroup.LayoutParams measurePicture(int drawable) {
        bitmap = BitmapFactory.decodeResource(getResources(), drawable);
        height= bitmap.getHeight();
        width= bitmap.getWidth();
        x = NotifyService.this.getResources().getDisplayMetrics().widthPixels;
        float ratio=(float) height/(float) width;
        ViewGroup.LayoutParams params = picture.getLayoutParams();
        params.width = x;
        params.height = (int) (ratio * x);
        return params;
    }

    private boolean isL18() {
        DisplayMetrics dm = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(dm);
        int width = dm.widthPixels;// 屏幕宽度(像素)
        //int height = dm.heightPixels;
        float density = dm.density;//屏幕密度(0.75 / 1.0 / 1.5)
        //int densityDpi = dm.densityDpi;//屏幕密度dpi(120 / 160 / 240)
        //屏幕宽度算法:屏幕宽度(像素)/屏幕密度
        int screenWidth = (int) (width/density);//屏幕宽度(dp)
        Log.i("ScreenWidth" , String.valueOf(width));
        if(width > 1440) {
            return true;
        }
        return false;
    }

    private boolean isNull(View view) {
        return view == null;
    }


    class NotifyBroadCastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if("PictureNotify".equals(action)) {
                int code1 = intent.getIntExtra("notDisplay" ,4);
                int code2 = intent.getIntExtra("leftDisplay",4);
                int code3 = intent.getIntExtra("rightDisplay" , 4);
                if(code1 == 1) {
                    //picture.setVisibility(View.GONE);
                    if(flag) {
                        manager.removeView(view);
                        remoteViews.setTextViewText(R.id.button2 , "显示");
                        notificationManager.notify(1,notification);

                        flag = false;
                    } else {
                        manager.addView(view , viewParam);
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        flag = true;
                    }


                    Log.i("CODE1" , String.valueOf(code1));
                } else if(code2 == 2) {
                    if(flag) {
//                        manager.removeView(view);
//                        flag = false;
                    } else {
                        manager.addView(view , viewParam);
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        flag = true;
                    }

                    Log.i("CODE2" , String.valueOf(code2));
                    if(isL18()) {
                        ViewGroup.LayoutParams params = measurePicture(R.drawable.l18left);
                        picture.setLayoutParams(params);
                        picture.setImageResource(R.drawable.l18left);
                    } else {
                        picture.setImageResource(R.drawable.phoneleft);
                    }
                } else if(code3 == 3) {
                    //picture.setVisibility(View.VISIBLE);
                    //picture.setAlpha((float) 0.5);
                    if(flag) {
                        //manager.removeView(view);
                        //flag = false;
                    } else {
                        remoteViews.setTextViewText(R.id.button2 , "不显示");
                        notificationManager.notify(1,notification);
                        manager.addView(view , viewParam);
                        flag = true;
                    }
                    Log.i("CODE3" , String.valueOf(code3));
                    if (isL18()) {
                        ViewGroup.LayoutParams params = measurePicture(R.drawable.l18right);
                        picture.setLayoutParams(params);
                        picture.setImageResource(R.drawable.l18right);
                    } else {
                        picture.setImageResource(R.drawable.phoneright);
                    }
                }
            }
        }
    }
}

这里的代码参考的别人的代码,因为在启动的监控上无法控制service的生命周期,故而使用了LiveCycle(不会)所以参考别人的 /xyx
两个工具类:
ViewModleMain.kt

object ViewModleMain : ViewModel() {
    //悬浮窗口创建 移除  基于无障碍服务
    var isShowWindow = MutableLiveData<Boolean>()
    //悬浮窗口创建 移除

    var isShowSuspendWindow = MutableLiveData<Boolean>()

    //悬浮窗口显示 隐藏
    var isVisible = MutableLiveData<Boolean>()

}

Utils.kt

object Utils {
    const val REQUEST_FLOAT_CODE=1001
    /**
     * 跳转到设置页面申请打开无障碍辅助功能
     */
    fun accessibilityToSettingPage(context: Context) {
        //开启辅助功能页面
        try {
            val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            context.startActivity(intent)
        } catch (e: Exception) {
            val intent = Intent(Settings.ACTION_SETTINGS)
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            context.startActivity(intent)
            e.printStackTrace()
        }
    }

    /**
     * 判断Service是否开启
     *
     */
    fun isServiceRunning(context: Context, ServiceName: String): Boolean {
        if (TextUtils.isEmpty(ServiceName)) {
            return false
        }
        val myManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val runningService =
            myManager.getRunningServices(1000) as ArrayList<ActivityManager.RunningServiceInfo>
        for (i in runningService.indices) {
            if (runningService[i].service.className == ServiceName) {
                return true
            }
        }
        return false
    }

    /**
     * 判断悬浮窗权限权限
     */
   private fun commonROMPermissionCheck(context: Context?): Boolean {
        var result = true
        if (Build.VERSION.SDK_INT >= 23) {
            try {
                val clazz: Class<*> = Settings::class.java
                val canDrawOverlays =
                    clazz.getDeclaredMethod("canDrawOverlays", Context::class.java)
                result = canDrawOverlays.invoke(null, context) as Boolean
            } catch (e: Exception) {
                Log.e("ServiceUtils", Log.getStackTraceString(e))
            }
        }
        return result
    }

    /**
     * 检查无障碍服务权限是否开启
     */
    fun checkAccessibilityPermission(context: Activity) : Boolean{
        return isServiceRunning(context, NotifyService::class.java.canonicalName)
    }

}

接下来是MainActivity.java

public class MainActivity extends AppCompatActivity {

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent2 = new Intent(MainActivity.this, NotifyService.class);

                if(Settings.canDrawOverlays(MainActivity.this)) {
//                    Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
//                    startActivity(intent);
                    if(!Utils.INSTANCE.checkAccessibilityPermission(this)){
                        Utils.INSTANCE.accessibilityToSettingPage(this);
                    }
                    ViewModleMain.INSTANCE.isShowWindow().postValue(true);

                } else {
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
                    startActivity(intent);
                }
                finish();

    }
}

关于在手机上怎么打开无障碍服务:

一般在手机的设置->更多设置->无障碍里面

Android 申请悬浮窗权限 android创建悬浮窗_java_02


Android 申请悬浮窗权限 android创建悬浮窗_android_03

打开相应服务就可以了