setContentView()做了什么事情?
Posted 哈特谢普苏特
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了setContentView()做了什么事情?相关的知识,希望对你有一定的参考价值。
在onCreate()方法中,我们一般都会遇到setContentView(),那么它到底做了什么?
一个活动继承有两种方式,分别是直接继承Activity,或者继承AppCompatActivity,setContentView()方法的执行就是由这两个基类完成的。
1.Activity
在Activity的setContentView方法主要做了两件事情,首先是通过getWindow获取到window(这个window实际上是phonewindow),调用window的setContentView()方法,其次调用initWindowDecorActionBar()尽心bar的初始化操作。
(1) getWindow
mWindowActivity的成员变量,可以看到在Activity的attach()方法中,mWindow被赋值为PhoneWindow类型。
那么Activity的attach()方法又是什么时候执行的呢?
在ActivityThread的 performLaunchActivity()方法中,首先通过createBaseContextForActivity(r)获取到基本的context,紧接着通过 activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent)创建当前的activity,最后调用activity的attach方法将 mInstrumentation、activity、context绑定起来。
(2) setContentView
由于getWindow()获取到的实际上是PhoneWindow,因此去看PhoneWindow的setContentView()
如上图所示,PhoneWindow的setContentView()方法只要做了三件事情。调用installDecor()方法创建decor,调用mLayoutInflater.inflate(),最后将mContentParentExplicitlySet设置为true。
(a)installDecor()
installDocor()方法中,调用了generateDecor(-1)方法和 generateLayout(mDecor)方法,generateDecor()方法实际上就是获取applicationContext并通过applicationContext创建DecorView。
generateLayout()方法中调用了requestFeature()方法
而requestFeature()方法中首先判断mContentParentExplicitlySet标志位是否为true,如果是的话则抛出异常。
generateLayout()方法中实际上有很多xml模板,如下图 中使用R.layout.screen_simple
R.layout.screen_simple.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
R.layout.screen_simple.xml实际上是一个linearLayout布局,内部有viewStub和FrameLayout,其中FrameLayout的id就是content,最终 generateLayout()方法将id为content的FrameLayout赋值给类型为ViewGroup的contentParent并返回
整个结构如下图所示
(b) mLayoutInflater.inflate()
inflate()方法多次重载,如果是merge布局,则调用rInflate()方法,否则调用createViewFromTag()方法创建rootView
在rInflate()方法中也会调用createViewFromTag()方法
createViewFromTag()方法会判断当前的name中是否还有"."如(constraintLayout就是这种类型,或者自定义的view)调用 onCreateView()方法,否则调用createView()方法。
LayoutInflater 是个抽象类,并没有onCreateView()方法的具体 实现,真正实现该方法的是PhoneLayoutInflater,onCreateView()方法内部其实也调用了createView()。其中for循环中的sClassPrefixList如下图。
createView()方法在LayoutInflater中实现,经过重载后,createView()方法内部通过反射得到当前view的全类名,并通过反射得到的构造器创建实例
注:好多细节小点:
a.如果使用merge布局,则merge布局需要作为根布局,且参数为true,rInflate()方法中有说明
(c) 将mContentParentExplicitlySet设置为true
(3) initWindowDecorActionBar()
2.AppCompatActivity
setContentView()是 AppCompatActivity中的方法
发现是getDelegate().setContentView()
实际上getDelegate()得到的是AppCompatDelegateImpl,因此去看的setCntentView()方法
发现首先调用了ensureSubDecor()方法,然后通过mSubDecor找到id为content的控件,清除其中所有的views - removeAllViews()并将当前view添加到容器中。
ensureSubDecor()方法中通过createSubDecor()创建了mSubDecor
createSubDecor()方法很长,首先ensureWindow(),其次调用了mWindow.getDecorView()
ensureWindow()中调用attachToWindow()方法,而mWindow就是在attachToWindow()中被赋值的。传入的参数mHost实际上是AppCompatActivity,被强转成Activity,并获取activity的window,实际上就是PhoneWindow。
AppCompatDelegateImpl的构造器中由于此时传入的window为null,因此不会调用attachToWindow()方法
当ensureWindow()方法调用结束后,调用mWindow.getDecorView()。即创建DecorView的过程,同Activity创建DecorView的过程一样。在createSubDecor()中,执行完这两个方法后,开始进行其它操作。
上图这段红框代码的含义是假设使用abc_screen_simple.xml,通过decorView获取到id为action_bar_activity_content的ContentFrameLayout,并使用PhoneWindow寻找id为content的控件并将其赋值给类型为ViewGroup的windowContentView,将windowContentView控件的id设置为NO_ID,然后将decorView获取到id为action_bar_activity_content的ContentFrameLayout的id设置为content。
R.layout.abc_screen_simple.xml
<androidx.appcompat.widget.FitWindowsLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/action_bar_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.ViewStubCompat
android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/abc_action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/abc_screen_content_include" />
</androidx.appcompat.widget.FitWindowsLinearLayout>
abc_screen_content_include.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.appcompat.widget.ContentFrameLayout
android:id="@id/action_bar_activity_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</merge>
友情链接:
以上是关于setContentView()做了什么事情?的主要内容,如果未能解决你的问题,请参考以下文章
java.lang.String 的 + 号操作到底做了什么事情?