欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新

setContentView源码分析,

来源: 开发者 投稿于  被查看 9179 次 评论:36

setContentView源码分析,


当我们在Activity中调用setContentView,它到底做了什么呢

override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)
}

我们跳转到AppCompatActivity的setContentView方法

@Override
public void setContentView(@LayoutRes int layoutResID) {
    //返回了AppCompatDelegate对象
    getDelegate().setContentView(layoutResID);
}

然后又调用了getDelegate().setContentView()

@NonNull
public AppCompatDelegate getDelegate() {
    if (mDelegate == null) {
	//跳转到create()方法可以看到,该方法返回的是一个AppCompatDelegateImpl对象
        mDelegate = AppCompatDelegate.create(this, this);
    }
    return mDelegate;
}

我们直接去AppCompatDelegateImpl中查看setContentView方法,可以看到,Activity中的

setContentView最后调用了AppCompatDelegateImpl的setContentView方法

@Override
public void setContentView(View v) {
    ensureSubDecor();
    ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    mOriginalWindowCallback.onContentChanged();
}

ensureSubDecor又调用了createSubDecor方法

private ViewGroup createSubDecor() {
    ....
    //步骤1
    //mWindow是PhoneWindow,该对象是在Activity.java attach()方法中初始化
    //该方法会触发DecorView的创建
    mWindow.getDecorView();
    ...
    //根据不同的情形加载不同的布局文件来初始化subDecor,具体过程可以不细究
    ...
    //步骤2
    //可以先看完之后的步骤可以再来看这里
    //调用PhoneWindow.setContentView()方法,将创建好的subDecor添加到mContentParent,
    //mContentParent是在installDecor方法中调用generateLayout()创建出来的
    //在之后的Android版本中,我们的布局其实是添加到了subDecor中,而subDecor又会添加到
    //PhoneWindow.mContentParent,mContentParent其实是DecorView的一个子View,初始化过程见installDecor方法
    mWindow.setContentView(subDecor);
}

mWindow.getDecorView会调用PhoneWindow的getDercorView(),里面会调用installDecor()

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
	//该方法会直接new DecorView()
        mDecor = generateDecor(-1);
	...
	...
    }
    if (mContentParent == null) {
        //生成一个FrameLayout,用于添加AppCompatDelegateImpl.createSubDecor()
        //中创建的subDecor,该view是在setContentView中添加的,见步骤2
        mContentParent = generateLayout(mDecor);
        final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);

        if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
	...
	...
    }

generateDecor()方法会创建一个DecorView,继承自FrameLayout,是PhoneWindow的内部类,

新版本的Android源码已独立出来

protected DecorView generateDecor(int featureId) {
    ...
    ...
    return new DecorView(context, featureId, this, getAttributes());
}

generateLayout方法根据不同的主题样式来加载不同系统的布局,

最终返回一个FrameLayout对象,可以通过findViewById(android.R.id.content)获取

protected ViewGroup generateLayout(DecorView decor){
   ...
   ...
   ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
   ...
   ...
   return contentParanet;
}

回到步骤2

最后我们回到AppCompatDelegateImpl.setContentView中去

@Override
public void setContentView(int resId) {
    ensureSubDecor();

    //这个对象就是PhoneWindow下面的generateLayout()创建出来的FrameLayout
    ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();

    //最后将我们传进来的布局文件实例化,然后添加进去
    LayoutInflater.from(mContext).inflate(resId, contentParent);

    //最后会回调onContentChanged()生命周期方法
    mOriginalWindowCallback.onContentChanged();
}

setContentView流程

总结:

setContentView()会触发DecorView的创建,最后会将我们的布局文件添加到DecorView,完成布局文件的加载,

这也就是为什么我们在onCreate()方法中获取不到view的宽高,因为该过程只加载了布局,并没有对布局View进行大小的测量.

用户评论