转载请注明出处:http://blog.csdn.net/guxiao1201/article/details/41517871
上篇博客实现圆角对话框样式的Activity中提到,若需实现圆角对话框Activity,需要在Activity的onAttachedToWindow()函数中做文章,那么就想问:
一、onAttachedToWindow在Activity生命周期中的位置
根据之前分析API的套路,看onAttachedToWindow在Android文档中怎样介绍。
首先看Window.Callback中关于onAttachedToWindow的介绍。
Called when the window has been attached to the window manager. See View.onAttachedToWindow()
for more information.
好吧,官方把我引导到了View中,那么恭敬不如从命,看View中怎么说。
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
然后通过Google找到了一张比较详细的Activity生命周期图,也印证了我们的实验结论。
详见http://staticfree.info/~steve/complete_android_fragment_lifecycle.svg
二、为什么要在onAttachedToWindow中修改窗口尺寸
为什么网上很多教程一定要在onAttachedToWindow()里修改高宽而不在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);
放到onCreate中进行测试,结果在lp.gravity = Gravity.CENTER;这行报了空指针异常,所以view.getLayoutParams()获取的LayoutParams是空,那么问题来了!为什么onCreate()中DecorView的LayoutParams是空而onAttachedToWindow()中就不为空?要高清这个问题就要看DecorView在什么时候设置的LayoutParam。
从博客Android应用窗口的视图对象的创建过程分析中发现源码
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 the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) { try { willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { } } if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); 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); } } ...... } ...... } ...... }
原来在ActivityThread执行handleResumeActivity时就会为PhoneWindow(r.activity.getWindow)中的DecorView设置LayoutParam,并且通过源码发现handleResumeActivity函数首先会执行performResumeActivity,此时会调用Activity的onResume()生命周期函数,这时问题就比较清晰了,看来只要在Activity的onResume生命周期后就能获取DecorView的LayoutParam,进而可以设置高度和宽度了。根据上面贴出的生命周期图,onResume()后面是onAttachedToWindow(),并且onAttachedToWindow只会调用一次,不会受用户操作行为影响。所以在onAttachedToWindow中进行窗口尺寸的修改再合适不过了。
总结: