Window和WindowManager
bugly也写了一篇更详细的
浅析Android的窗口
windowmanager 使用 demo1
2
3
4
5
6
7
8
9
10
11
12
13
14WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Button mFloatingButton = new Button(this);
mFloatingButton.setText("click me");
WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
PixelFormat.TRANSPARENT);
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_SHOW_WHEN_LOCKED;
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
mWindowManager.addView(mFloatingButton, mLayoutParams);
注意:小米等手机进行了framework的修改,所以不能正常显示悬浮窗,需要自己去设置中心里面设置允许弹窗。
一、WindowManager.LayoutParams的flag
Flags参数表示window的属性,通过这些选项可以控制Window的显示特性
1 | /** Window flag: this window won't ever get key input focus, so the |
二、WindowManager.LayoutParams的type
Type 表示Window的类型。有3种类型。
- 应用Window。z-index 1-99
- 子Window。不能单独存在,需要附属在父Window上。比如PopupWindow(TYPE_APPLICATION_PANEL),Dialog(查看源代码)。z-index 1000-1999
- 系统Window。Toast和状态栏等。z-index 2000-2999
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/**
* Start of types of sub-windows. The {@link #token} of these windows
* must be set to the window they are attached to. These types of
* windows are kept next to their attached window in Z-order, and their
* coordinate space is relative to their attached window.
*/
public static final int FIRST_SUB_WINDOW = 1000;
/**
* Window type: a panel on top of an application window. These windows
* appear on top of their attached window.
*/
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
/**
* Start of system-specific window types. These are not normally
* created by applications.
*/
public static final int FIRST_SYSTEM_WINDOW = 2000;
/**
* Window type: the status bar. There can be only one status bar
* window; it is placed at the top of the screen, and all other
* windows are shifted down so they are below it.
* In multiuser systems shows on all users' windows.
*/
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
/**
* Window type: transient notifications.
* In multiuser systems shows only on the owning user's window.
*/
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
z-index越大,显示越在前面。有些type需要声明权限后使用。
利用type可以做悬浮窗,之前UC 浏览器出来的弹窗被广大安卓爱好者反编译研究。
秋百万对悬浮窗的总结
Android无需权限显示悬浮窗, 兼谈逆向分析app
三、WindowManager类关系图
WindowManage继承自ViewManager,分别处理添加、删除、更新view的操作。WindowManager操作的都是View,而实际上Window是一个抽象的概念,是以View的形式存在的。View变化,与之对应的Window也会随之变化。如下面的代码,通过给View设置OnClickListener,改变LayoutParams.x,y就可以改变Window的位置。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@Override
public boolean onTouch(View v, MotionEvent event) {
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
break;
}
case MotionEvent.ACTION_MOVE: {
int x = (int) event.getX();
int y = (int) event.getY();
mLayoutParams.x = rawX;
mLayoutParams.y = rawY;
mWindowManager.updateViewLayout(mFloatingButton, mLayoutParams);
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
return false;
}
四、详解Window
Window是抽象类,从[Android是怎么加载布局的?]中我们发现PhoneWindow是Window的实例,而这个PhoneWindow是在com.android.internal.policy.impl包中,我们无法直接使用,只能是通过WindowManager来访问。
window的添加
需要使用WindowManager的addView来实现,WindowManagerImpl实现了WindowManager.
1 | /** |
WindowManagerGlobal.java
1 | private final ArrayList<View> mViews = new ArrayList<View>(); |
根据代码,WindowManagerGlobal.addView()主要分以下几步:
1、检查参数是否合法
2、创建ViewRootImpl,并将View添加到列表mRoots中;view添加到mViews中;param添加到mParams中。
3、通过root.setView来完成Window的添加过程。ViewRootImpl的setView方法会触发requestLayout,然后就是喜闻乐见的view绘制过程了。接下来通过windowSession完成window的添加过程。最终交给WMS来处理。
ViewRootImpl.java1
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
42public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//省略n多代码
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
window的删除和更新
这个就没啥好说的了。跟上面太类似了。删除通过VIewRootImpl来完成删除操作,进行垃圾回收等等。通过WMS删除window;回调Window的onDetachedFromWindow等等。更新就是重新设置LayoutParam,ViewRootImpl通过schedualTraversals对View进行重绘。结合Android源码和开发艺术探索第8章去看吧。
五、Window的创建过程
View是Android中视图的呈现方式,但View是附着在Window这个抽象上面,因此有视图的地方就有window,比如Activity、Dialog、Toast、Popupwindow等。
其实在Android是怎么加载布局的?中已经描述了window的创建过程,就是那个PhoneWindow。。
简单描述Activity启动过程:
1、ActivityThread.performLaunchActivity
ActivityThread.java1
2
3
4
5
6private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
activity.attach(...)
...
}2、创建Window,设置回调
PhoneWindow1
2
3
4
5
6
7
8
9final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
mWindow = PolicyManager.makeNewWindow(this);3、当Activity的setContentView被调用时,就会创建Decor等。
- 最后Activity组件创建完成之后,通过调用ActivityThread类的handleResumeActivity将它激活。进而调用Activity的makeVisible方法显示控件。
1
2
3
4
5
6
7
8void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
参考资料:
开发艺术探索:第8章
Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析