知识点 参考链接
关于悬浮窗的创建
- 首先需要获取WindowManager
WindowManager manager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
- 为窗口添加参数(大小、位置、类型…)
WindowManager.LayoutParams viewParam = new WindowManager.LayoutParams();
然后可以设置窗口的Params,比如viewParams.type = WindowManager.LayoutParams.Type_APPLICATION_OVERLAY
等
- 创建一个View作为Window的布局和UI
View view = LayoutInflater.from(this).inflate(R.layout.view2, null);
- 将之前创建的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<