发布于2022年11月4日3年前 当我们在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进行大小的测量.
创建帐户或登录后发表意见