简介
首先看Window.Callback中关于onAttachedToWindow的介绍:
public abstract void onAttachedToWindow ()
Called when the window has been attached to the window manager. See View.onAttachedToWindow() for more information.
好吧,官方把我引导到了View中,那么恭敬不如从命,看View中怎么说:
protected void onAttachedToWindow ()
This is called when the view is attached to a window. At this point it has a Surface and will start drawing. Note that this function is guaranteed to be called beforeonDraw(android.graphics.Canvas), however it may be called any time before the first onDraw – including before or after onMeasure(int, int).
从API说明可知,当View附加到窗体时,也就是View和Window绑定时就会调用这个函数,此时将会有一个Surface进行绘图之类的逻辑。并且发现Window.CallBack是一个接口类,而且官方引导到了View中,那么可以大胆判断View实现了Window.CallBack的回调方法,那么View和Window之间的关系便可以有个初步猜测。下篇博客再具体讨论DecorView和Window之间的关系。
onAttachedToWindow在Activity整个生命周期中的位置。
将Activity各个生命周期打上log,然后看LogCat中的结果,可得出:
生命周期为 onCreate->onStart->onResume->onAttachedToWindow
找了张比较完整的activity生命周期图,印证了上面的结论
为什么要在onAttachedToWindow中修改窗口尺寸?
为什么网上很多教程一定要在onAttachedToWindow()里修改高宽而不在onCreate中?
将代码放到onCreate中进行测试:
View view = getWindow().getDecorView();
WindowManager.LayoutParams lp = (WindowManager.LayoutParams)view.getLayoutParams();
lp.gravity = Gravity.CENTER;
lp.width = (dm.widthPixels * 4) / 5;
lp.height = (dm.widthPixels * 4) / 5;
getWindowManager().updateViewLayout(view,lp);
结果发现,lp.gravity = Gravity.CENTER 这行报了空指针异常,所以view.getLayoutParams()获取的LayoutParams是空,那么问题来了!为什么onCreate()中DecorView的LayoutParams是空而onAttachedToWindow()中就不为空?要搞清这个问题就要看DecorView在什么时候设置的LayoutParam。
public final class ActivityThread {
......
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
......
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow(); // PhoneWindow
View decor = r.window.getDecorView(); // PhoneWindow 中的DecorView
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
......
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l); // 给DecorView设置LayoutParam
}
}
......
}
......
}
......
}
原来在ActivityThread执行handleResumeActivity时就会为PhoneWindow中的DecorView设置LayoutParam,并且通过源码发现handleResumeActivity函数首先会执行performResumeActivity,此时会调用Activity的onResume()生命周期函数。看来只要在Activity的onResume生命周期后就能获取DecorView的LayoutParam,进而可以设置高度和宽度了。根据上面贴出的生命周期图,onResume()后面是onAttachedToWindow(),并且onAttachedToWindow只会调用一次,不会受用户操作行为影响。所以在onAttachedToWindow中进行窗口尺寸的修改再合适不过了。
总结
- onAttachedToWindow运行在onResume之后;
- DecorView的LayoutParams是在ActivityThread的handleResumeActivity中设置的,并且该函数会调用Activity的onResume生命周期,所以在onResume之后可以设置窗体尺寸;