The setContentView method is called in the Activity,
Windows concepts
All views of Android phones are displayed through windows, such as common activities, Dialog, PopupWindow, and Toast. Their views are attached to Windows, so we can say that -- Window is the direct manager of the View.
Loading layout of Activity
The loading layout of the Activity is started by calling the setContentView method in onCreate:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_layout); ButterKnife.bind(this); }
There is a setContentView overload method in the Activity. One is layoutid, and the other is directly passed into the view:
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); } public void setContentView(View view) { getWindow().setContentView(view); initWindowDecorActionBar(); } public void addContentView(View view, ViewGroup.LayoutParams params) { getWindow().addContentView(view, params); initWindowDecorActionBar();}
In fact, the setContentView method of PhoneWindow is called (PhoneWindow is the specific implementation class of Window ). InitWindowDecorActionBar:
@ 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) {// if mContentParent is an air conditioner, use installDecor to initialize installDecor ();} else if (! HasFeature (FEATURE_CONTENT_TRANSITIONS) {// If the content has been loaded (not the first time) and no animation is required, removeAllViews () mContentParent. removeAllViews ();} if (hasFeature (FEATURE_CONTENT_TRANSITIONS) {final Scene newScene = Scene. getSceneForLayout (mContentParent, layoutResID, getContext (); // If FEATURE_CONTENT_TRANSITIONS is set after Content is added, add Scene to enable transitionTo (newScene) excessively );} else {// you can see the view of mContentParent as the parent of layoutResID, The layoutResID root width/height parameter is valid for mLayoutInflater. inflate (layoutResID, mContentParent);} mContentParent. requestApplyInsets (); final Callback cb = getCallback (); if (cb! = Null &&! IsDestroyed () {cb. onContentChanged () ;}@override public void setContentView (View view) {// so it is always MATCH_PARENT, so the width/height of the view is invalid setContentView (view, new ViewGroup. layoutParams (MATCH_PARENT, MATCH_PARENT);} @ Override public void setContentView (View view, ViewGroup. layoutParams params) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when t Heme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent = null) {installDecor ();} else if (! HasFeature (FEATURE_CONTENT_TRANSITIONS) {mContentParent. removeAllViews ();} if (hasFeature (FEATURE_CONTENT_TRANSITIONS) {view. setLayoutParams (params); final Scene newScene = new Scene (mContentParent, view); transitionTo (newScene);} else {// Add this view to mContentParent, at the same time, layoutparam is MATCH_PARENT mContentParent. addView (view, params);} mContentParent. requestApplyInsets (); final Callback cb = getC Allback (); if (cb! = Null &&! IsDestroyed () {cb. onContentChanged ();}}
The source code shows that mContentParent is initialized in the installDecor method:
Private void installDecor () {if (mDecor = null) {// initialize the mDecor View mDecor = generateDecor (); mDecor. setDescendantFocusability (ViewGroup. FOCUS_AFTER_DESCENDANTS); mDecor. setIsRootNamespace (true); if (! MInvalidatePanelMenuPosted & mInvalidatePanelMenuFeatures! = 0) {mDecor. postOnAnimation (mInvalidatePanelMenuRunnable) ;}} if (mContentParent = null) {// initialize mContentParent = generateLayout (mDecor); ......} through the mDecor object );.....}.....}
You can see that the DecorView is generated by using the generateDecor method. This DecorView is actually the root View of all application Windows:
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {...}
The mContentParent object is initialized in the generateLayout method:
Protected ViewGroup generateLayout (DecorView decor) {// Apply data from current theme. apply the current theme data. the following series of if statements are used to determine the currently loaded form of TypedArray a = getWindowStyle () according to the theme you set ();......... // set style to Window_windowNo or Window_windowActionBar if (. getBoolean (R. styleable. window_windowNo, false) {requestFeature (FEATURE_NO _);} else if (. getBoolean (R. styleable. window_windowActionBar, false) {// Don't allow an ac Tion bar if there is no. requestFeature (FEATURE_ACTION_BAR );}.... // set whether to enable full screen if (. getBoolean (R. styleable. window_windowFullscreen, false) {setFlags (FLAG_FULLSCREEN, FLAG_FULLSCREEN &(~ GetForcedWindowFlags ()));}...... // a large number of if Statements to determine style resources // here, based on targetSdkVersion in gradle, determine whether to load the menu bar final Context context = getContext (); final int targetSdk = context.getApplicationInfo().tar getSdkVersion; final boolean targetPreHoneycomb = targetSdk <android. OS. build. VERSION_CODES.HONEYCOMB; final boolean targetPreIcs = targetSdk <android. OS. build. VERSION_CODES.ICE_CREAM_SANDWICH; final boolean tar GetPreL = targetSdk <android. OS. Build. VERSION_CODES.LOLLIPOP; final boolean targetHcNeedsOptions = context. getResources (). getBoolean (R.bool.tar regular); final boolean noActionBar =! HasFeature (FEATURE_ACTION_BAR) | hasFeature (FEATURE_NO _); if (targetPreHoneycomb | (targetPreIcs & targetHcNeedsOptions & noActionBar) {setNeedsMenuKey (WindowManager. layoutParams. NEEDS_MENU_SET_TRUE);} else {setNeedsMenuKey (WindowManager. layoutParams. NEEDS_MENU_SET_FALSE);} // non-floating windows on high-end devices must be placed below the system bar, so you must know the visibility changes of these windows. // Non-floating windows on high end devices must put up decor beneath the system bars and // therefore must know about visibility changes of those. if (! MIsFloating & ActivityManager. isHighEndGfx () {if (! TargetPreL & a. getBoolean (R. styleable. Window_windowDrawsSystemBarBackgrounds, false) {setFlags (FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS &~ GetForcedWindowFlags ());}}...... // omit multiple if statements to determine WindowManager. layoutParams params = getAttributes ();...... // omit other loading resources // Inflate the window decor // Add the layout to DecorView. As mentioned above, DecorView is inherited from FrameLayout and is also a ViewGroup, // when we first created it, we only called new DecorView, and there was nothing in it. The following step creates a layout topic based on the Feature set by the user. // For example, if I call requestWindowFeature (Window. FEATURE_NO _), // getLocalFeatures is used to obtain the configured feature and then load the corresponding layout. At this time, the topic without a title bar is loaded. // This is why Theme or requesetFeature () must be set before setContentView. int layoutResource; // remember the requestFeature mentioned above, and then you can get it through the getLocalFeatures method .. In fact, you can call requestFeature int features = getLocalFeatures (); // System. out. println ("Features: 0x" + Integer. toHexString (features); if (features & (1 <FEATURE_SWIPE_TO_DISMISS ))! = 0) {layoutResource = R. layout. screen_swipe_dismiss;} else if (features & (1 <FEATURE_LEFT_ICON) | (1 <FEATURE_RIGHT_ICON )))! = 0) {if (mIsFloating) {TypedValue res = new TypedValue (); getContext (). getTheme (). resolveAttribute (R. attr. dialogIconsDecorLayout, res, true); layoutResource = res. resourceId;} else {layoutResource = R. layout. screen _ icons;} // XXX Remove this once action bar supports these features. removeFeature (FEATURE_ACTION_BAR); // System. out. println ("Icons! ");} Else if (features & (1 <FEATURE_PROGRESS) | (1 <FEATURE_INDETERMINATE_PROGRESS )))! = 0 ...... // a large number of else if condition judgments} else {// Embedded, so no decoration is needed. layoutResource = R. layout. screen_simple; ③ // System. out. println ("Simple! ");} MDecor. startChanging (); // select the layout to be created and added to View in = mLayoutInflater in DecorView. inflate (layoutResource, null); // Add a subview to DecorView, that is, mContentParent decor. addView (in, new ViewGroup. layoutParams (MATCH_PARENT, MATCH_PARENT); mContentRoot = (ViewGroup) in ;......... // obtain the view with id = content as contenparent ViewGroup contentParent = (ViewGroup) findViewById (ID_ANDROID_CONTENT );......... mDecor. finishChanging (); return contentParent ;}
As you can see, the main function of the above method is to select different window root layout files for the window based on the style modification type of the window. MDecor is used as the Root View to add the root layout of the window, and then get the FrameLayout with the id of content and return it to the mContentParent object. Therefore, the installDecor method generates mDecor and mContentParent objects. If you want to set the window style, you must put it in front of setContentView:
RequestWindowFeature (Window. FEATURE_NO _); // setContentView (R. layout. test_layout) is obtained in the getLocalFeatures method );
View tree structure:
As a Root View, DecorView has three sub-views. The View with id statusBarBackgroud and id navigationBarBackground indicates the status bar at the top of the mobile phone and the navigation bar at the bottom of the mobile phone, respectively. Then there is a LinearLayout. This LinearLayout selects different window root layout files for the window based on the window style modifier type: when the window style is No and there is No actionbar.
LinearLayout includes ViewStub of @ + id/action_mode_bar_stub and FrameLayout of @ android: id/content. In setContentView, the following two methods in the setContentView method of PhoneWindow use FrameLayout as the Root View of main_activity.xml:
1. mLayoutInflater. inflate (layoutResID, mContentParent );
2. mContentParent. addView (view, params );
Inflate actually calls addview internally. If this view is a viewgroup all the time, the viewgroup adds all the sub-views in the view, so the view forms a view level.
DecorView is a top-level View that can mark the entire screen, including the status bar, navigation bar, content area, and so on. Here, mContentParent refers to the content area displayed on the screen, and the activity_main.xml layout we set is actually in a FrameLayout with the id of content. This FrameLayout is also the mContentParent that we mentioned earlier! (If we look at the source code, we will find that more than screen_simple.xml, screen_toobar.xml, screen _. xml, and other layout files contain FrameLayout with the content id)
Screen_simple.xml source code: R. layout. screen_simple is the layout when theme is set to NoBar. That is, LinearLayout in DecorView.
<Framelayout android: foreground = "? Android: attr/windowContentOverlay "android: foregroundgravity =" fill_horizontal | top "android: foregroundinsidepadding =" false "android: id =" @ android: id/content "android: layout_height = "match_parent" android: layout_width = "match_parent"> </framelayout>
Screen _. xml. xml source code: the layout when the window style is No and there is No actionbar, that is, LinearLayout in DecorView
<framelayout android:layout_height="?android:attr/windowSize" android:layout_width="match_parent">
</framelayout> <framelayout android:foreground="?android:attr/windowContentOverlay" android:foregroundgravity="fill_horizontal|top" android:id="@android:id/content" android:layout_height="0dip" android:layout_weight="1" android:layout_width="match_parent"> </framelayout>
The root viewgroup of all views is the preceding decorview.
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. ...... // TODO Push resumeArgs into the activity for consideration ActivityClientRecord r = performResumeActivity(token, clearHide); if (r != null) { ...... // 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. ...... // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. ...... // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { ...... if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } } ...... } else { // If an exception was thrown when trying to resume, then // just end this activity. ...... } }
Handler mechanism we know that the main method of activityThread will be started when Activity is started, and the Logoff of the main thread will be created in this method. After the ActivityThread main method is called, The ActivityThread class javasmlaunchactivity is called to create the Activity component to be started. During the Activity component creation process, the window object and view object will be created for the Activity component. After the Activity component is created, it will be activated by calling the handleResumeActivity of the ActivityThread class. (SetContentView is called in onCreate, so all Views except decorView are added .) In handleResumeActivity, decorView is added to the handleResumeActivity method and r. activity. makeVisible () is called ().
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
On the other hand, wm. addView actually calls the addView method in WindowManagerGlobal and creates the ViewRootImpl object .. ViewRootImpl is very familiar with wood. In the process of drawing a View, it starts from the performTraversals method of ViewRootImpl, and then goes through the measurement, layout, and Painting Process in sequence .. In fact, invalidate also calls the javasmtraversals method of ViewRootImpl.
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ............ ViewRootImpl root; View panelParentView = null; ............ root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); }