全局悬浮窗在许多应用中都能见到,点击Home键,小窗口仍然会在屏幕上显示。如微信视频,360软件清理等等
添加权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"tools:ignore="ProtectedPermissions" />
当API Level>=23的时候就要动态的申请权限了,判断是否能够绘制悬浮窗:
Settings.canDrawOverlays(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);Intent intent = new Intent();intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + getPackageName()));startActivityForResult(intent, 0);}
全局的悬浮窗是通过WindowManager来绘制已达到能够全局显示的效果,而WindowManager的addView方法还需要一个WindowManager.LayoutParam对象作为参数,此处Android 8.0之后的需要适配一下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;}
为了能够使悬浮窗脱离activity全局显示,因此这里使用Service来启动悬浮窗。 界面触发悬浮窗代码如下:
public class MainActivity extends AppCompatActivity {public static boolean isStart = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button btFloatingWindow = findViewById(R.id.bt_floating_window);btFloatingWindow.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startFloatingService();}});}@SuppressLint("ShowToast")public void startFloatingService() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);Intent intent = new Intent();intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + getPackageName()));startActivityForResult(intent, 0);return;}if (!isStart) {startService(new Intent(MainActivity.this, FloatingService.class));}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == 0) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();if (!isStart)startService(new Intent(MainActivity.this, FloatingService.class));}}}}
创建悬浮窗权限
public class FloatingService extends Service {private WindowManager windowManager;private WindowManager.LayoutParams layoutParams;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {showFloatingWindow();return super.onStartCommand(intent, flags, startId);}@SuppressLint("InflateParams")private void showFloatingWindow() {MainActivity.isStart = true;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {// 获取WindowManager服务windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);// 设置LayoutParamlayoutParams = new WindowManager.LayoutParams();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;}layoutParams.format = PixelFormat.RGBA_8888;layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;//宽高自适应layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;//显示的位置layoutParams.x = 300;layoutParams.y = 300;// 新建悬浮窗控件View view = LayoutInflater.from(this).inflate(R.layout.float_window, null);view.setOnTouchListener(new FloatingOnTouchListener());// 将悬浮窗控件添加到WindowManagerwindowManager.addView(view, layoutParams);}}private class FloatingOnTouchListener implements View.OnTouchListener {private int x;private int y;@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouch(View view, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:x = (int) event.getRawX();y = (int) event.getRawY();break;case MotionEvent.ACTION_MOVE:int nowX = (int) event.getRawX();int nowY = (int) event.getRawY();int movedX = nowX - x;int movedY = nowY - y;x = nowX;y = nowY;layoutParams.x = layoutParams.x + movedX;layoutParams.y = layoutParams.y + movedY;// 更新悬浮窗控件布局windowManager.updateViewLayout(view, layoutParams);break;default:break;}return false;}}@Overridepublic void onDestroy() {super.onDestroy();MainActivity.isStart = false;}}
