reoger的记录

--以后的你会感激现在那么努力的自己

0%

悬浮窗使用小结

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|1
99|
|子 Window|10001999|
|应用 Window|2000
2999|
这些层级范围对应着 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

参考链接