window简介 Window 有三种类型,分别是应用 Window、子 Window 和系统 Window。应用类 Window 对应一个 Acitivity,子 Window 不能单独存在,需要依附在特定的父 Window 中,比如常见的一些 Dialog 就是一个子 Window。系统 Window是需要声明权限才能创建的 Window,比如 Toast 和系统状态栏都是系统 Window。
Window 是分层的,每个 Window 都有对应的 z-ordered,层级大的会覆盖在层级小的 Window 上面,这和 HTML 中的 z-index 概念是完全一致的。在三种 Window 中,应用 Window 层级范围是 199,子 Window 层级范围是 10001999,系统 Window 层级范围是 20002999,我们可以用一个表格来直观的表示: |Window|层级| |—|—-| |应用 Window|199| |子 Window|10001999| |应用 Window|20002999| 这些层级范围对应着 WindowManager.LayoutParams 的 type 参数,如果想要 Window 位于所有 Window 的最顶层,那么采用较大的层级即可,很显然系统 Window 的层级是最大的,当我们采用系统层级时,需要声明权限。
悬浮窗简单使用 悬浮窗通过WindowManager
来实现,它提供的三个重要的方法为:
1 2 3 public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view);
我们只需要将需要显示的view添加到windowManager
中即可。但是因为是系统层的显示,在addView
时需要必要的权限
1 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
并且在android 6.0及以上的版本时,还需要申请动态权限,这里就不展开了。
悬浮窗工具类 通过一个工具类实现通过悬浮窗实现类似于toast的效果。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 public class FloatWindowManager { private WindowManager.LayoutParams mParams; private WindowManager mWindowManager; private boolean mIsRunning = false; private boolean mIsAutoDisMiss = true; private TimerTask mTask; private Timer mTimer; public final static int DEFAULT_FINISH_TIME = 2000; private int mAutoDismissTime = DEFAULT_FINISH_TIME; public FloatWindowManager() { mWindowManager = (WindowManager) HostHelper.getAppContext().getSystemService(Context.WINDOW_SERVICE); mParams = new WindowManager.LayoutParams(); } public FloatWindowManager initParams() { if (mParams != null) { if (Build.VERSION.SDK_INT >= 26) { mParams.type = WindowManager.LayoutParams.TYPE_PHONE; } else { mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } mParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SCALED | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; mParams.format = PixelFormat.RGBA_8888; } return this; } public FloatWindowManager setLocation(int x,int y,int width,int height){ if (mParams!=null){ mParams.x = x; mParams.y = y; mParams.height = height; mParams.width = width; } return this; } public FloatWindowManager setGravity(int gravity){ if (mParams!=null){ mParams.gravity = gravity; } return this; } public FloatWindowManager setAnim(int anim){ if (mParams!=null){ mParams.windowAnimations = anim; } return this; } public FloatWindowManager setAutoDismiss(boolean autoDismiss){ this.mIsAutoDisMiss = autoDismiss; return this; } public boolean show(View view) { if(CloudConfigUtils.getToastActivityShow()){ try { if (mWindowManager != null && mParams!=null && !mIsRunning) { mWindowManager.addView(view, mParams); if (mIsAutoDisMiss){ finishByTime(view); } Log.d("FloatWindowManager"," view is added!"); mIsRunning = true; return true; } } catch (Exception e) { mIsRunning = false; Log.d("FloatWindowManager", "addView go something wrong!"); return false; } } return false; } private void finishByTime(final View view){ mTimer = new Timer(); mTask = new TimerTask() { @Override public void run() { finisSelf(view); } }; mTimer.schedule(mTask, mAutoDismissTime); } private void finisSelf(final View view){ try { if (mWindowManager !=null && mParams!=null && mIsRunning){ mWindowManager.removeView(view); mIsRunning = false; if (mTask!=null) mTask = null; if (mTimer!=null) mTimer = null; } Log.d("FloatWindowManager"," view is remove!"); } catch (Exception e) { mIsRunning = false; if (mTask!=null) mTask = null; if (mTimer!=null) mTimer = null; Log.d("FloatWindowManager", "remove go something wrong!"); } } }
调用也很简单,只需要一行代码:
1 2 3 4 5 6 7 8 9 //自定义view final AccountToastLayout layout = new AccountToastLayout(mContext); //通知测量自定义view的宽和高 layout.measure(View.MeasureSpec.UNSPECIFIED,View.MeasureSpec.UNSPECIFIED); int weight = layout.getMeasuredWidth(); final int height = layout.getMeasuredHeight(); FloatWindowManager windowManager = new FloatWindowManager(); windowManager.initParams().setAutoDismiss(true).setAnim(R.style.anim).setGravity(Gravity.BOTTOM|Gravity.END) .setLocation(0,y,weight,height).show(layout);
windowManager中相关属性的介绍 type字段介绍
value
key
作用
备注
2031
TYPE_ACCESSIBILITY_OVERLAY
用于拦截用户交互而不更改窗口,可访问性服务可以内省。
added in API level 22
2
TYPE_APPLICATION
正常的应用程序窗口。token必须是标识activity所属window的标记。
added in API level 1
1003
TYPE_APPLICATION_ATTACHED_DIALOG
类似于TYPE_APPLICATION_PANEL
,但窗口的布局与顶层窗口的布局相同,而不是其容器的子窗口。
added in API level 3
1001
TYPE_APPLICATION_MEDIA
用于显示媒体的窗口(例如视频)。这些窗口显示在其附加窗口后面。
added in API level 3
….
flag字段介绍 看这里https://developer.android.com/reference/android/view/WindowManager.LayoutParams
参考链接