Android App interface mounting and display and source code analysis, androidapp
- Preface
- Getting started
- View tree
- Source code analysis
Preface
I haven't written a blog for a long time, and I feel a little unfamiliar.
Generally, people have better requirements for themselves so that they can make better progress. We will continue to provide you with more useful knowledge.
My personal skills are limited. If you feel that my blog is useful to you, leave a comment to encourage you. If there is something wrong, please take a look!
Note: The IDE I use here is Android Studio.
Getting started
I believe that everyone has created many Demo projects when learning Android, so the following code is very familiar to everyone.
Public class MainActivity extends Activity {@ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); // This code is added for ease of explanation. The purpose is to remove the title. RequestWindowFeature (Window. FEATURE_NO_TITLE); setContentView (R. layout. activity_main );}}
The main purpose of the code is to layout the fileactivity_main.xml
Display to the screen. The effect after running is as follows:
UsedView.inflate()
A friend of the method knows that this method can fill an XML layout file into a View object, and the above Code can be converted to this:
View view = View.inflate(this, R.layout.activity_main, null);setContentView(view);
Is it a little strange?
Google, as a great company, is definitely a programmer in it. Their Naming of methods can certainly play the role of "knowing what you mean.
The problem arises. We clearly set the activity_main layout for MainActivity. We should use this naming method.setView(view)
It looks more professional!
Why is it used here?setContentView(view)
What about it?
In fact, all our la s are placed in a FrameLayout layout. Because the content is set for FrameLayout, usesetContentView(view)
Isn't it strange?
Add the id to activity_main.xml and obtain its father to see what it is.
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); View view = View.inflate(this, R.layout.activity_main, null); setContentView(view); RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.rl_main); ViewParent parent = relativeLayout.getParent(); System.out.println(parent); }
The output is as follows:
As you can see, after activity_main.xml is converted into a View, it is indeed placed in FrameLayout. As you can see, I believe you understand whysetContentView(view)
InsteadsetView()
.
Of course, all of them are appetizers so far!
What does the Android system do for us from application startup to page display? Please continue to look down!
View tree
To find out what additional work the Android system has done for us and how the view tree of the application is, you need to use an SDK tool, which is placed in the SDK directory:/SDK/tools/hierarchyviewer. bat.
After you double-click it, you will see the following view, where black displays the current mobile phone or simulator running APP.
Double-click the black entry and you will see the following view. This is the view tree.
This tool can help us show the View hierarchy of the project. It seems much refreshed because the title bar is removed from the Code. The selected green color is the layout file R. layout. main, and the red in the lower right of the layout file area on the screen.
As we have mentioned above, the Relativilayout layout is nested in a FrameLayout with content id. This can also be confirmed inonCreate()
The method for setting the layout is calledsetContetnView(view)
InsteadsetView
(View. WhileViewStub
It is a lazy space to load. The default value is 0, so we don't have to worry about it.
From the left side, when we select PhoneWindow $ DectorView, we will find that the screen space represented by the entire APP on the right will be highlighted in red as the border, this indirectly demonstrates that DectorView is the root layout (for the moment, there is a ViewRoot at the top ).
In this view,PhoneWindow$DectoryView
In this form,$
Previously, it represents a class,$
It represents an internal class in this class.
Source code analysis
Understanding this is far from enough. Let's look at the source code. Here, my source code version is API22 and the IDE is Android Studio.
ClicksetContentView(view)
Method. Check the internal implementation and you will find that the calledgetWindow()
Then, the internalsetContentView()
Method.
public void setContentView(View view) { getWindow().setContentView(view); initWindowDecorActionBar();}
WhilegetWindow()
A mWindow is returned, and mWindow is a variable of Window. Let's continue and take a look at the source code of Widnow. We can find that Window is an abstract class,setContentView()
It is also an abstract method that requires subclass implementation.
public abstract void setContentView(int layoutResID);
If we find that Window is an abstract class, there will certainly be its implementation class. In AS, Ctrl + H, we will find that the default implementation class of Window is PhoneWindow, because PhoneWindow is hidden by Google, in Eclipse, you cannot see it directly.
FindsetContentView()
Method. The mContentParent must be null when the app is loaded for the first time.installDecor()
. FinishedinstallDecor()
After the method, mContentParent also has a value (we can infer that mContentParent is FrameLayout), in the second★, Use the layout filler to convert activity_main.xml into a View object and place it in mContentParent.
@Overridepublic void setContentView(int layoutResID) { if (mContentParent == null) { // ★ 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); } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); }}
installDecor()
It is the meaning of initialization decoration. It can be inferred that the Android system has done a lot for us in this method. Let's follow up. This method has more than 200 rows. Here, only the important logic is given.
Private void installDecor () {if (mDecor = null ){//★Generate decoration mDecor = generateDecor (); mDecor. setDescendantFocusability (ViewGroup. FOCUS_AFTER_DESCENDANTS); mDecor. setIsRootNamespace (true); if (! MInvalidatePanelMenuPosted & mInvalidatePanelMenuFeatures! = 0) {mDecor. postOnAnimation (mInvalidatePanelMenuRunnable) ;}} if (mContentParent = null ){//★MContentParent = generateLayout (mDecor); // Set up decor part of UI to ignore fitsSystemWindows if appropriate. mDecor. makeOptionalFitsSystemWindows ();......}}
When this method is entered for the first time, mDecor must be null, so it will inevitably entergenerateDecor()
Method. Continue to follow up.
protected DecorView generateDecor() { return new DecorView(getContext(), -1);}
The method is very simple. A new DecorView object, the first parameter is the context, so what does the second-1 represent? Open the inheritance tree and you can see that DecorView inherits from a FrameLayout, while FrameLayout is the child of ViewGroup.
The following information can be found in ViewGroup:
public static final int MATCH_PARENT = -1;public static final int WRAP_CONTENT = -2;
-1 indicates that the Dé corView object fills the screen by default, which is consistent with what we see in Hierarchy-Viwer. After obtaining mDecor, continue to go down. Because the mContentParent must be nullgenerateLayout(mDecor);
And pass in mDecor. We continue to follow up. The meaning of the method is that the layout is generated based on the decoration, and the source code is very long. Here we only provide the key.
Protected ViewGroup generateLayout (DecorView decor) {// Apply data from current theme. // omit several codes ......... // Inflate the window decor. int layoutResource; 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. dialogTitleIconsDecorLayout, res, true); layoutResource = res. resourceId;} else {layoutResource = R. layout. screen_title_icons;} // XXX Remove this once action bar supports these features. removeFeature (FEATURE_ACTION_BAR); // System. out. println ("Title Icons! ");} Else if (features & (1 <FEATURE_PROGRESS) | (1 <FEATURE_INDETERMINATE_PROGRESS )))! = 0 & (features & (1 <FEATURE_ACTION_BAR) = 0) {// Special case for a window with only a progress bar (and title ). // XXX Need to have a no-title version of embedded windows. layoutResource = R. layout. screen_progress; // System. out. println ("Progress! ");} Else if (features & (1 <FEATURE_CUSTOM_TITLE ))! = 0) {// Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) {TypedValue res = new TypedValue (); getContext (). getTheme (). resolveAttribute (R. attr. dialogCustomTitleDecorLayout, res, true); layoutResource = res. resourceId;} else {layoutResource = R. layout. screen_custom_title;} // XXX Remove this once action bar supports these feat Ures. removeFeature (FEATURE_ACTION_BAR);} else if (features & (1 <FEATURE_NO_TITLE) = 0) {// If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) {TypedValue res = new TypedValue (); getContext (). getTheme (). resolveAttribute (R. attr. dialogTitleDecorLayout, res, true); layoutResource = res. resourceId;} else if (feat Ures & (1 <FEATURE_ACTION_BAR ))! = 0) {layoutResource = a. getResourceId (R. styleable. Window_windowActionBarFullscreenDecorLayout, R. layout. screen_action_bar);} else {//★★★★★LayoutResource = R. layout. screen_title;} // System. out. println ("Title! ");} Else if (features & (1 <FEATURE_ACTION_MODE_OVERLAY ))! = 0) {layoutResource = R. layout. screen_simple_overlay_action_mode;} else {// Embedded, so no decoration is needed. layoutResource = R. layout. screen_simple; // System. out. println ("Simple! ");} MDecor. startChanging (); View in = mLayoutInflater. inflate (layoutResource, null); decor. addView (in, new ViewGroup. layoutParams (MATCH_PARENT, MATCH_PARENT); mContentRoot = (ViewGroup) in; // omitting code ...... return contentParent ;}
We usedrequestWindowFeature(Window.FEATURE_NO_TITLE);
Set a decoration for the form. We can directly go to the key code. We will continue to make judgments hereFeaturesWhat is it? After a bunch of judgments, it will reach the above Code★★★★★.
if ((features & (1 << FEATURE_NO_TITLE)) == 0) { ... ... // ★★★★★ layoutResource = R.layout.screen_title;}
In this way, go to the if and get a layoutResource, which is actually an XML layout. Let's take a look at the layout of R. layout. screen_title.
R. layout. the content of screen_title is as follows. A LinearLayout contains two FragmenLayout and one ViewStub. Because we have set no Title, the second FrameLayout will not be displayed, the third is the frame layout of our id = content. Are you getting a little clearer?
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:fitsSystemWindows="true"> <!-- Popout bar for action modes --> <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" android:theme="?attr/actionBarTheme" /> <FrameLayout android:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize" style="?android:attr/windowTitleBackgroundStyle"> <TextView android:id="@android:id/title" style="?android:attr/windowTitleStyle" android:background="@null" android:fadingEdge="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>
IngenerateLayout(DecorView decor)
In the following code, add the View object converted from the screen_titlle.xml file to decor, that isPhoneWindow$DecorView
.
View in = mLayoutInflater.inflate(layoutResource, null);decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));mContentRoot = (ViewGroup) in;
Finally, return to the PhoneWindowsetContentView(resId)
In the methodmLayoutInflater.inflate(layoutResID, mContentParent);
Place our layout file in mContentParent.
Now, the mounting and display of the entire View interface is complete.
If you feel that this blog post is helpful to you, please leave a message. If you have any mistakes, please make a lot of bricks. Thank you !!!