本篇博文介绍三个方面的知识
转载请征求本人同意并请注明出处 :本文出自海天之蓝的博客
chapter One:认识Activity的布局
chapter Two:启动activity时的布局--从setContentView说起
chapter Three:总结说明
View这个东西,是构成页面Activity最基本的元素,所以可想而知他到底有多重要。在研究activity组件的view加载之前,先整体认
识下activity的布局,有助于更好的去理解setContentView方法
对于研究布局这种东西,必须要掌握一些视图工具,在这里推荐一个sdk查看视图的工具sdk\tools\hierarchyviewer,随意找一个界
面去查看activity的view视图
在这个activity界面中我把导航栏给隐藏了,所以不存在导航栏,根据这张图的话大致可以看到一个activity的布局,再结合对
setContentView的研究,可以总结出activity的布局图如下:
从这张activity的布局图可以看到:一个activity对应一个应用窗口mWindow,应用窗口mWindow包括activity的顶级view是
mDecorView,mDecorView包括状态栏statusbar和导航栏navigationbar以及要加载activity布局的view-----------------------
mDecorContentParent,该view又包括一个标题栏titlebar和activity的内容布局contentparent。在contentParent中就是该
activity的view树。
对activity的布局大致有个了解之后,就开始去分析activity启动后加载view的流程
对于activity的布局的加载大致分为两部分,一部分是加载view,另一部分是将view绑定到应用窗口Window。其中这两个步
骤中将view绑定到window是在启动activity时完成的操作,是将mDecor绑定到window。然后再往mDecor中添加各种view。对于
activity的启动过程留待以后进行分析,现在分析加载view---始于Activity.java的setContentView方法,看一下加载view的流程。
可以看到代码流程很简单,从Activity.java的setContentView方法进入,到PhonewWindow.java的setContentView方法进行一系
列处理,接下来进入代码进行分析
1,Activity.java的setContentView方法,代码路径\android\frameworks\base\core\java\android\app
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
view添加到activity。意思就是填充一个资源文件,加载view。做了两件事儿
重点研究第一步:getWindow().setContentView方法。
首先一个问题,为什么我要说getWindow.setContentView调用的是PhoneWindow中的setContentView方法??
解疑:查看getWindow方法
public Window getWindow() { return mWindow; }
返回的是activity的mWindow对象,对于mWindow对象的创建也是在Activity.java中的attach方法中
final 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) { 。。。。 。。。。。。。 。。。。。 。。。。 //创建mWindow对象 mWindow = new PhoneWindow(this); //设置mWindow回调为该activity,这个在接下来setContentView的分析中会触发回调 mWindow.setCallback(this); }
同时,进入到Window.java中也可以看到这一点:
/** * Abstract base class for a top-level window look and behavior policy. An * instance of this class should be used as the top-level view added to the * window manager. It provides standard UI policies such as a background, title * area, default key processing, etc. * * <p>The only existing implementation of this abstract class is * android.view.PhoneWindow, which you should instantiate when needing a * Window. */ public abstract class Window {
2,PhoneWindow.java中的setContentView方法,代码路径\android\frameworks\base\core\java\com\android\internal\policy
@Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { //实例化DecorView对象和mContentParent对象 installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); //执行切换动画 transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { //通知activity内容已经改变,触发onContentChanged方法 cb.onContentChanged(); } }
先来总结一下代码的流
在新启动一个activity时mContentParent还未绑定id,此时mContentParent为null。从代码流程图中可以看出setContentView做了三件事
installDecor实例化DecorView对象和mContentParent对象
填充layout文件
通知activity布局已经改变
为什么说是通知activity布局已经改变呢?这是因为在Activity.java的attach方法中mWindow对象设置了callback为this,所以在getCallback时获取到的cb为当前与该window对应的activity。
3,PhoneWindow.java中的installDecor方法分析-----实例化DecorView对象和mContentParent对象
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); ............... } } if (mContentParent == null) { mContentParent = generateLayout(mDecor); // Set up decor part of UI to ignore fitsSystemWindows if appropriate. } } if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) { mDecor.setBackgroundFallback(mBackgroundFallbackResource); } 。。。。。。。。 。。。。。。。。 } }
在创建一个activity时mDecor和mContentParent均为null
接下里对installDecor中某些代码做一些分析
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable)去开启初始化menu菜单的线程
在这里说明一句,为什么requestFeature要写在setContentView前面,这是因为在调用setContentView时会获取到window的各种
feature进行一些判断设置。
4,PhoneWindow.java中的generateLayout方法研究
第一步:首先是获取到window的布局style
TypedArray a = getWindowStyle();
第二步,获取到各种属性并进行requestFeature的设置
第三步,通过获取到的window的布局去获取window的各种属性,并根据window的各种属性去选择不同的layout的文件,比如标题栏
是否隐藏,window是悬浮窗还是全屏,等等问题。当然因为在3.0和4.0以及5.0对于menukey的支持不同,所以会有一个与版本相关
的 一个判断。至于这个版本之间有什么不同可以参考总结说明中列出来的文件。
其实generatelayout就做了一件事,那就是根据window的各种属性去获取不同的xml文件。
Activity在启动加载布局共有两个操作
如有问题,欢迎学习交流,微信公众号---fanfan程序媛