来源 http://www.cnblogs.com/mengdd/p/3824782.html

编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识、前端、后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过!

WindowManager介绍

  通过Context.getSystemService(Context.WINDOW_SERVICE)可以获得 WindowManager对象。

  每一个WindowManager对象都和一个特定的 Display绑定。

  想要获取一个不同的display的WindowManager,可以用 createDisplayContext(Display)来获取那个display的 Context,之后再使用:

Context.getSystemService(Context.WINDOW_SERVICE)来获取WindowManager。


  使用WindowManager可以在其他应用最上层,甚至手机桌面最上层显示窗口。

  调用的是WindowManager继承自基类的addView方法和removeView方法来显示和隐藏窗口。具体见后面的实例。


  另:API 17推出了Presentation,它将自动获取display的Context和WindowManager,可以方便地在另一个display上显示窗口。


WindowManager实现悬浮窗例子

声明权限

  首先在manifest中添加如下权限:

            
  1. android:name="android.permission.SYSTEM_ALERT_WINDOW" />

 注意:在MIUI上需要在设置中打开本应用的”显示悬浮窗”开关,并且重启应用,否则悬浮窗只能显示在本应用界面内,不能显示在手机桌面上。



服务获取和基本参数设置

            
  1. // 获取应用的Context
  2. mContext = context.getApplicationContext();
  3. // 获取WindowManager
  4. mWindowManager = (WindowManager) mContext
  5. .getSystemService(Context.WINDOW_SERVICE);


参数设置:

            
  1. final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
  2. // 类型
  3. params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
  4. // WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
  5. // 设置flag
  6. int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
  7. // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
  8. // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件
  9. params.flags = flags;
  10. // 不设置这个弹出框的透明遮罩显示为黑色
  11. params.format = PixelFormat.TRANSLUCENT;
  12. // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
  13. // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按
  14. // 不设置这个flag的话,home页的划屏会有问题
  15. params.width = LayoutParams.MATCH_PARENT;
  16. params.height = LayoutParams.MATCH_PARENT;
  17. params.gravity = Gravity.CENTER;


点击和按键事件

  除了View中的各个控件的点击事件之外,弹窗View的消失控制需要一些处理。

  点击弹窗外部可隐藏弹窗的效果,首先,悬浮窗是全屏的,只不过最外层的是透明或者半透明的:

  布局如下:

            
  1. <?xml version="1.0" encoding="utf-8"?>
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:layout_gravity="center"
  6. android:background="@color/darken_background"
  7. android:gravity="center"
  8. android:orientation="vertical" >
  9. android:id="@+id/popup_window"
  10. android:layout_width="@dimen/dialog_window_width"
  11. android:layout_height="@dimen/dialog_window_height"
  12. android:background="@color/white"
  13. android:orientation="vertical" >
  14. android:id="@+id/title"
  15. android:layout_width="match_parent"
  16. android:layout_height="@dimen/dialog_title_height"
  17. android:gravity="center"
  18. android:text="@string/default_title"
  19. android:textColor="@color/dialog_title_text_color"
  20. android:textSize="@dimen/dialog_title_text_size" />
  21. android:id="@+id/title_divider"
  22. android:layout_width="match_parent"
  23. android:layout_height="2dp"
  24. android:layout_below="@id/title"
  25. android:background="@drawable/dialog_title_divider" />
  26. android:id="@+id/content"
  27. android:layout_width="match_parent"
  28. android:layout_height="wrap_content"
  29. android:layout_below="@id/title_divider"
  30. android:gravity="center"
  31. android:padding="@dimen/dialog_content_padding_side"
  32. android:text="@string/default_content"
  33. android:textColor="@color/dialog_content_text_color"
  34. android:textSize="@dimen/dialog_content_text_size" />
  35. android:layout_width="match_parent"
  36. android:layout_height="wrap_content"
  37. android:layout_alignParentBottom="true"
  38. android:orientation="horizontal"
  39. android:paddingBottom="@dimen/dialog_content_padding_bottom"
  40. android:paddingLeft="@dimen/dialog_content_padding_side"
  41. android:paddingRight="@dimen/dialog_content_padding_side" >
  42. android:id="@+id/negativeBtn"
  43. android:layout_width="wrap_content"
  44. android:layout_height="wrap_content"
  45. android:layout_weight="1"
  46. android:background="@drawable/promote_window_negative_btn_selector"
  47. android:focusable="true"
  48. android:padding="@dimen/dialog_button_padding"
  49. android:text="@string/default_btn_cancel"
  50. android:textColor="@color/dialog_negative_btn_text_color"
  51. android:textSize="@dimen/dialog_button_text_size" />
  52. android:id="@+id/positiveBtn"
  53. android:layout_width="wrap_content"
  54. android:layout_height="wrap_content"
  55. android:layout_marginLeft="18dp"
  56. android:layout_weight="1"
  57. android:background="@drawable/promote_window_positive_btn_selector"
  58. android:focusable="true"
  59. android:padding="@dimen/dialog_button_padding"
  60. android:text="@string/default_btn_ok"
  61. android:textColor="@color/dialog_positive_btn_text_color"
  62. android:textSize="@dimen/dialog_button_text_size" />
  63. popupwindow.xml


点击外部可消除设置:

            
  1. // 点击窗口外部区域可消除
  2. // 这点的实现主要将悬浮窗设置为全屏大小,外层有个透明背景,中间一部分视为内容区域
  3. // 所以点击内容区域外部视为点击悬浮窗外部
  4. final View popupWindowView = view.findViewById(R.id.popup_window);// 非透明的内容区域
  5. view.setOnTouchListener(new OnTouchListener() {
  6. @Override
  7. public boolean onTouch(View v, MotionEvent event) {
  8. LogUtil.i(LOG_TAG, "onTouch");
  9. int x = (int) event.getX();
  10. int y = (int) event.getY();
  11. Rect rect = new Rect();
  12. popupWindowView.getGlobalVisibleRect(rect);
  13. if (!rect.contains(x, y)) {
  14. WindowUtils.hidePopupWindow();
  15. }
  16. LogUtil.i(LOG_TAG, "onTouch : " + x + ", " + y + ", rect: "
  17. + rect);
  18. return false;
  19. }
  20. });


点击Back键可隐藏弹窗:

  注意Flag不能设置WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE。

            
  1. // 点击back键可消除
  2. view.setOnKeyListener(new OnKeyListener() {
  3. @Override
  4. public boolean onKey(View v, int keyCode, KeyEvent event) {
  5. switch (keyCode) {
  6. case KeyEvent.KEYCODE_BACK:
  7. WindowUtils.hidePopupWindow();
  8. return true;
  9. default:
  10. return false;
  11. }
  12. }
  13. });



完整效果


  完整代码:

MainActivity

            
  1. package com.example.hellowindow;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.widget.Button;
  8. public class MainActivity extends Activity {
  9. private Handler mHandler = null;
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState) {
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.activity_main);
  14. mHandler = new Handler();
  15. Button button = (Button) findViewById(R.id.button);
  16. button.setOnClickListener(new OnClickListener() {
  17. @Override
  18. public void onClick(View v) {
  19. mHandler.postDelayed(new Runnable() {
  20. @Override
  21. public void run() {
  22. WindowUtils.showPopupWindow(MainActivity.this);
  23. }
  24. }, 1000 * 3);
  25. }
  26. });
  27. }
  28. }


WindowUtils

            
  1. package com.example.hellowindow;
  2. import android.content.Context;
  3. import android.graphics.PixelFormat;
  4. import android.graphics.Rect;
  5. import android.view.Gravity;
  6. import android.view.KeyEvent;
  7. import android.view.LayoutInflater;
  8. import android.view.MotionEvent;
  9. import android.view.View;
  10. import android.view.View.OnKeyListener;
  11. import android.view.View.OnTouchListener;
  12. import android.view.WindowManager;
  13. import android.view.View.OnClickListener;
  14. import android.view.WindowManager.LayoutParams;
  15. import android.widget.Button;
  16. /**
  17. * 弹窗辅助类
  18. *
  19. * @ClassName WindowUtils
  20. *
  21. *
  22. */
  23. public class WindowUtils {
  24. private static final String LOG_TAG = "WindowUtils";
  25. private static View mView = null;
  26. private static WindowManager mWindowManager = null;
  27. private static Context mContext = null;
  28. public static Boolean isShown = false;
  29. /**
  30. * 显示弹出框
  31. *
  32. * @param context
  33. * @param view
  34. */
  35. public static void showPopupWindow(final Context context) {
  36. if (isShown) {
  37. LogUtil.i(LOG_TAG, "return cause already shown");
  38. return;
  39. }
  40. isShown = true;
  41. LogUtil.i(LOG_TAG, "showPopupWindow");
  42. // 获取应用的Context
  43. mContext = context.getApplicationContext();
  44. // 获取WindowManager
  45. mWindowManager = (WindowManager) mContext
  46. .getSystemService(Context.WINDOW_SERVICE);
  47. mView = setUpView(context);
  48. final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
  49. // 类型
  50. params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
  51. // WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
  52. // 设置flag
  53. int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
  54. // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
  55. // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件
  56. params.flags = flags;
  57. // 不设置这个弹出框的透明遮罩显示为黑色
  58. params.format = PixelFormat.TRANSLUCENT;
  59. // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
  60. // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按
  61. // 不设置这个flag的话,home页的划屏会有问题
  62. params.width = LayoutParams.MATCH_PARENT;
  63. params.height = LayoutParams.MATCH_PARENT;
  64. params.gravity = Gravity.CENTER;
  65. mWindowManager.addView(mView, params);
  66. LogUtil.i(LOG_TAG, "add view");
  67. }
  68. /**
  69. * 隐藏弹出框
  70. */
  71. public static void hidePopupWindow() {
  72. LogUtil.i(LOG_TAG, "hide " + isShown + ", " + mView);
  73. if (isShown && null != mView) {
  74. LogUtil.i(LOG_TAG, "hidePopupWindow");
  75. mWindowManager.removeView(mView);
  76. isShown = false;
  77. }
  78. }
  79. private static View setUpView(final Context context) {
  80. LogUtil.i(LOG_TAG, "setUp view");
  81. View view = LayoutInflater.from(context).inflate(R.layout.popupwindow,
  82. null);
  83. Button positiveBtn = (Button) view.findViewById(R.id.positiveBtn);
  84. positiveBtn.setOnClickListener(new OnClickListener() {
  85. @Override
  86. public void onClick(View v) {
  87. LogUtil.i(LOG_TAG, "ok on click");
  88. // 打开安装包
  89. // 隐藏弹窗
  90. WindowUtils.hidePopupWindow();
  91. }
  92. });
  93. Button negativeBtn = (Button) view.findViewById(R.id.negativeBtn);
  94. negativeBtn.setOnClickListener(new OnClickListener() {
  95. @Override
  96. public void onClick(View v) {
  97. LogUtil.i(LOG_TAG, "cancel on click");
  98. WindowUtils.hidePopupWindow();
  99. }
  100. });
  101. // 点击窗口外部区域可消除
  102. // 这点的实现主要将悬浮窗设置为全屏大小,外层有个透明背景,中间一部分视为内容区域
  103. // 所以点击内容区域外部视为点击悬浮窗外部
  104. final View popupWindowView = view.findViewById(R.id.popup_window);// 非透明的内容区域
  105. view.setOnTouchListener(new OnTouchListener() {
  106. @Override
  107. public boolean onTouch(View v, MotionEvent event) {
  108. LogUtil.i(LOG_TAG, "onTouch");
  109. int x = (int) event.getX();
  110. int y = (int) event.getY();
  111. Rect rect = new Rect();
  112. popupWindowView.getGlobalVisibleRect(rect);
  113. if (!rect.contains(x, y)) {
  114. WindowUtils.hidePopupWindow();
  115. }
  116. LogUtil.i(LOG_TAG, "onTouch : " + x + ", " + y + ", rect: "
  117. + rect);
  118. return false;
  119. }
  120. });
  121. // 点击back键可消除
  122. view.setOnKeyListener(new OnKeyListener() {
  123. @Override
  124. public boolean onKey(View v, int keyCode, KeyEvent event) {
  125. switch (keyCode) {
  126. case KeyEvent.KEYCODE_BACK:
  127. WindowUtils.hidePopupWindow();
  128. return true;
  129. default:
  130. return false;
  131. }
  132. }
  133. });
  134. return view;
  135. }
  136. }


参考资料

  WindowManager:

http://developer.android.com/reference/android/view/WindowManager.html

  参考实例:

http://blog.csdn.net/deng0zhaotai/article/details/16827719

http://www.xsmile.net/?p=538

http://blog.csdn.net/guolin_blog/article/details/8689140

  简单说明:

  Android之Window、WindowManager与窗口管理:

http://blog.csdn.net/xieqibao/article/details/6567814

  Android系统服务-WindowManager:

http://blog.csdn.net/chenyafei617/article/details/6577940

进一步的学习:

  老罗的Android之旅:

  Android Activity的窗口对象Window的创建过程分析:

http://blog.csdn.net/luoshengyang/article/details/8223770

  窗口管理服务WindowManagerService的简要介绍和学习计划:

http://blog.csdn.net/luoshengyang/article/details/8462738

  Android核心分析之窗口管理:

http://blog.csdn.net/maxleng/article/details/5557758

更多相关文章

  1. Android(安卓)定时重复启动弹出窗口。
  2. Android败笔之ListView设置分割线颜色
  3. ListView设置的点点滴滴
  4. Ubuntu共享WiFi(AP)
  5. 主题背景色
  6. Android设置多个定时器时只有最后一个定时器有效的问题
  7. Android(安卓)有些视频设置为视频壁纸后不能全屏
  8. 微信踩坑集锦
  9. Android权限目录

随机推荐

  1. android EditText中inputType的属性列表
  2. android实现TextView多行文本滚动
  3. Android(安卓)Interface Definition Lang
  4. [Android] EditText 的 inputType属性(键
  5. android 字体的使用
  6. Android(安卓)TextView属性大全
  7. Android安全加密:消息摘要Message Digest
  8. Android文件的读写
  9. Android(安卓)PinnedHeaderListView 详解
  10. Android(安卓)Support兼容包详解