全局悬浮窗在许多应用中都能见到,点击Home键,小窗口仍然会在屏幕上显示。如微信视频,360软件清理等等


    添加权限

    1. <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    3. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
    4. tools:ignore="ProtectedPermissions" />

    当API Level>=23的时候就要动态的申请权限了,判断是否能够绘制悬浮窗:

    1. Settings.canDrawOverlays(this)
    1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
    2. Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
    3. Intent intent = new Intent();
    4. intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    5. intent.setData(Uri.parse("package:" + getPackageName()));
    6. startActivityForResult(intent, 0);
    7. }



    全局的悬浮窗是通过WindowManager来绘制已达到能够全局显示的效果,而WindowManager的addView方法还需要一个WindowManager.LayoutParam对象作为参数,此处Android 8.0之后的需要适配一下:

    1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    2. layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    3. } else {
    4. layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
    5. }

    为了能够使悬浮窗脱离activity全局显示,因此这里使用Service来启动悬浮窗。 界面触发悬浮窗代码如下:


    1. public class MainActivity extends AppCompatActivity {
    2. public static boolean isStart = false;
    3. @Override
    4. protected void onCreate(Bundle savedInstanceState) {
    5. super.onCreate(savedInstanceState);
    6. setContentView(R.layout.activity_main);
    7. Button btFloatingWindow = findViewById(R.id.bt_floating_window);
    8. btFloatingWindow.setOnClickListener(new View.OnClickListener() {
    9. @Override
    10. public void onClick(View v) {
    11. startFloatingService();
    12. }
    13. });
    14. }
    15. @SuppressLint("ShowToast")
    16. public void startFloatingService() {
    17. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
    18. Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
    19. Intent intent = new Intent();
    20. intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    21. intent.setData(Uri.parse("package:" + getPackageName()));
    22. startActivityForResult(intent, 0);
    23. return;
    24. }
    25. if (!isStart) {
    26. startService(new Intent(MainActivity.this, FloatingService.class));
    27. }
    28. }
    29. @Override
    30. protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    31. super.onActivityResult(requestCode, resultCode, data);
    32. if (requestCode == 0) {
    33. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
    34. Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();
    35. } else {
    36. Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
    37. if (!isStart)
    38. startService(new Intent(MainActivity.this, FloatingService.class));
    39. }
    40. }
    41. }
    42. }

    创建悬浮窗权限

    1. public class FloatingService extends Service {
    2. private WindowManager windowManager;
    3. private WindowManager.LayoutParams layoutParams;
    4. @Override
    5. public IBinder onBind(Intent intent) {
    6. return null;
    7. }
    8. @Override
    9. public int onStartCommand(Intent intent, int flags, int startId) {
    10. showFloatingWindow();
    11. return super.onStartCommand(intent, flags, startId);
    12. }
    13. @SuppressLint("InflateParams")
    14. private void showFloatingWindow() {
    15. MainActivity.isStart = true;
    16. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
    17. // 获取WindowManager服务
    18. windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    19. // 设置LayoutParam
    20. layoutParams = new WindowManager.LayoutParams();
    21. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    22. layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    23. } else {
    24. layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
    25. }
    26. layoutParams.format = PixelFormat.RGBA_8888;
    27. layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    28. //宽高自适应
    29. layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
    30. layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    31. //显示的位置
    32. layoutParams.x = 300;
    33. layoutParams.y = 300;
    34. // 新建悬浮窗控件
    35. View view = LayoutInflater.from(this).inflate(R.layout.float_window, null);
    36. view.setOnTouchListener(new FloatingOnTouchListener());
    37. // 将悬浮窗控件添加到WindowManager
    38. windowManager.addView(view, layoutParams);
    39. }
    40. }
    41. private class FloatingOnTouchListener implements View.OnTouchListener {
    42. private int x;
    43. private int y;
    44. @SuppressLint("ClickableViewAccessibility")
    45. @Override
    46. public boolean onTouch(View view, MotionEvent event) {
    47. switch (event.getAction()) {
    48. case MotionEvent.ACTION_DOWN:
    49. x = (int) event.getRawX();
    50. y = (int) event.getRawY();
    51. break;
    52. case MotionEvent.ACTION_MOVE:
    53. int nowX = (int) event.getRawX();
    54. int nowY = (int) event.getRawY();
    55. int movedX = nowX - x;
    56. int movedY = nowY - y;
    57. x = nowX;
    58. y = nowY;
    59. layoutParams.x = layoutParams.x + movedX;
    60. layoutParams.y = layoutParams.y + movedY;
    61. // 更新悬浮窗控件布局
    62. windowManager.updateViewLayout(view, layoutParams);
    63. break;
    64. default:
    65. break;
    66. }
    67. return false;
    68. }
    69. }
    70. @Override
    71. public void onDestroy() {
    72. super.onDestroy();
    73. MainActivity.isStart = false;
    74. }
    75. }