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

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

知识点 参考链接

关于悬浮窗的创建

  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)@Overridepublic 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")@Overridepublic 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);}@Overridepublic 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);}}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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)@Overrideprotected 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就可以解决此问题
但是当你开开心心使用的时候你会发现以下错误
经过查证一些资料之后发现这个类型必须和无障碍 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() {}@Overridepublic Lifecycle getLifecycle() {return lifecycleRegistry;}@SuppressLint("ServiceCast")@RequiresApi(api = 31)@Overridepublic 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);}}}}});}@Overrideprotected void onServiceConnected() {super.onServiceConnected();lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);}@Overridepublic boolean onUnbind(Intent intent) {lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);return super.onUnbind(intent);}@Overridepublic void onDestroy() {super.onDestroy();lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);}@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {}@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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_TASKcontext.startActivity(intent)} catch (e: Exception) {val intent = Intent(Settings.ACTION_SETTINGS)intent.flags = Intent.FLAG_ACTIVITY_NEW_TASKcontext.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 ActivityManagerval 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 = trueif (Build.VERSION.SDK_INT >= 23) {try {val clazz: Class<*> = Settings::class.javaval 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)@Overrideprotected 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窗口Window的创建(悬浮窗)相关推荐

  1. android动态申请悬浮框权限,Android创建悬浮窗的完整步骤

    在Android中想要创建悬浮窗分为三步 1.申请权限 2.使用服务启动悬浮窗 3.设置悬浮窗参数并添加进WindowManager 下面话不多说了,来一起看看详细的实现过程 申请权限 首先需要申请悬 ...

  2. Android检测是否有悬浮窗,Android 获取判断是否有悬浮窗权限的方法

    现在很多应用都会用到悬浮窗,很多国产rom把悬浮窗权限加入控制了,你就需要判断是否有悬浮窗权限,然后做对应操作. Android 原生有自带权限管理的,只是被隐藏了.看android源码在androi ...

  3. android悬浮动态权限,Android 获取判断是否有悬浮窗权限的方法

    现在很多应用都会用到悬浮窗,很多国产rom把悬浮窗权限加入控制了,你就需要判断是否有悬浮窗权限,然后做对应操作. Android 原生有自带权限管理的,只是被隐藏了.看android源码在androi ...

  4. android 魅族 toast,小米 TYPE_TOAST 悬浮窗无效的原因

    Android中的悬浮窗显示是一个非常棘手的问题,网上已经有很多解决方案了,大致归为下面两类:设置WindowManager.LayoutParams.type = TYPE_SYSTEM_ALERT ...

  5. android 悬浮窗口禁止横屏显示,悬浮窗强制设置屏幕方向|App开发交流区|研发交流|雨滴科技技术论坛 - Powered by Discuz!...

    最近在做平板上的一个程序,需要配合中通的app来控制扫描与分拣机的转动.然后中通的程序在平板上运行有一个问题, 就是app里设置了强制竖屏,不能跟随系统旋转应用屏幕方向,然后把系统里的屏幕方向写死,虽 ...

  6. android 实现录屏功能(悬浮窗)

    前言:网上关于录屏的介绍已经很多了,本篇文章也不过多介绍详细,主要是阐述其流程原理,输出demo,该demo样式仿照小米自带的系统录屏 1.如何录屏 Android中在5.0以上的版本中系统已经提供了 ...

  7. Android可拖动可吸附悬浮窗

    第一次写稿,写的不好,请大神多提建议 前言 前一段时间由于项目需要,写了一个可拖动可靠边吸附的悬浮窗,特意记录下来,方便大家一起学习 一.FloatingViewMagnet 悬浮窗的吸附管理类,代码 ...

  8. Android 屏幕录制时去除悬浮窗

    最近在用Android8.0开发一个屏幕录制的功能,要求录制的视频不能有录屏的控制悬浮窗. 录屏方案使用的是MediaRecorder.MediaProjection.VirtualDisplay. ...

  9. android 仿微信来电_Android 悬浮窗功能实现(微信语音通话悬浮窗效果实现)

    目录 1.基本介绍 2.代码示例 3.实现效果及便捷工具类 4.仿微信语音通话悬浮窗效果实现 4.1 需求分析及效果展示 4.2 实现 5.最后 1.基本介绍 Android 界面绘制都是通过 Win ...

最新文章

  1. oracle11g资源过低,Sun Cluster3.3+Oracle11g配置数据库资源问题
  2. flutter打开android界面,在已有Android项目中使用Flutter
  3. RIPv1和v2综合实验(CCNP阶段)
  4. error java on syntax token_解决Java“syntax error on token enum”问题
  5. 系统制成docker镜像_如何让Docker基础镜像变得更小?
  6. [MySQL FAQ]系列 -- 数据不算大,备份却非常慢
  7. Qt程序窗口关闭不退出而最小化到托盘的方法
  8. 洛谷2017-2月月赛
  9. HBuilder原生功能概述
  10. pdflib使用:pdf的分割与合并
  11. JRTPLib的编译步骤
  12. AXI5 new feature: support atomic transaction
  13. Java面试官经验谈:如何甄别候选人真实的能力,候选人如何展示值钱技能
  14. Day7-Python综合作业1(DataWhale)
  15. 基于java的人机猜拳游戏
  16. 帝国时代3如何快速实现增加建筑/农民数量上限
  17. 科创人·知乎CTO李大海:技术服务内容、商业化依赖内容,曾被「呵呵」难到挠头
  18. Docker 下载安装 Docker 配置镜像加速器
  19. 部署OpenStack云管理平台
  20. mac抹掉磁盘重装系统未能与服务器取得联系_macOS降级重装系统教程:手把手教你Mac降级不再难...

热门文章

  1. Java对马踏棋盘问题(骑士周游问题)的实现
  2. 响应式Web程序设计【15】
  3. 浅析搭建高速公路视频监控平台的建设方案及必要性
  4. 淘宝API_item_cat_get - 获得淘宝商品类目
  5. VIBE运动目标检测算法实现
  6. 主题:windows xp 系统CMD命令大全(一)
  7. 闭关之 C++ Template 笔记(一):PartⅠ基本概念(一)
  8. 深入浅出精讲面向对象设计七大原则,彻底领悟设计背后思想
  9. 为啥在VS中使用scanf函数会有警告呢?如何解决此问题?(如何添加#define _CRT_SECURE_NO_WARNINGS 1)
  10. mysql5.7出现:ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)问题解决