Android窗口Window的创建(悬浮窗)

本文详细介绍了如何在Android中创建悬浮窗,包括获取WindowManager、设置窗口参数、创建View布局,以及处理悬浮窗的手势穿透问题。针对Android R和S的不同行为,讨论了Window的type和透明度设置。此外,还讲解了使用Service启动悬浮窗,启用前台服务以保持悬浮窗显示,并介绍了如何应对无障碍窗口的问题,以及开启无障碍服务的步骤。
摘要由CSDN通过智能技术生成

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


知识点 参考链接

关于悬浮窗的创建

  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<
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值