Source code analysis of the View rendering mechanism in Android

Source: Internet
Author: User

Source code analysis of the View rendering mechanism in Android

 

I haven't written a blog for almost half a year. I think my work is busy, I think there is nothing worth writing, and I think I am getting lazy, however, I recently have some new ideas about the View rendering mechanism in Android, so I want to record it and share it with you. In the following blogs, I will share the following content:

1. Analyze the execution process of the measure (), layout (), and draw () functions in the View, and analyze the size measurement process and Position Calculation of the View in detail, and finally draw the process to the UI

2. The following uses LinearLayout as an example to explain the ViewGroup size calculation, location calculation, and drawing process.

3. a deeper understanding of the significance of LayoutParams

4. LayoutInflater analyzes the process of creating a View and analyzes the meaning of each parameter in the inflate (int resource, ViewGroup root, boolean attachToRoot) method.

Mastering the above knowledge points is of great significance to the custom View. As far as I know, the custom View is a required knowledge point during the interview process.

The preceding content is important to the View system in Android. The features of the View system mainly include the entire process from user input to message processing, and the rendering of the UI, I have also written several articles about user input messages and message processing. If you are interested, I can explain the following:

Android Touch event transfer mechanism: http://blog.csdn.net/yuanzeyao/article/details/37961997

Android system Touch event transfer mechanism: http://blog.csdn.net/yuanzeyao/article/details/38025165

Android system Key event transfer mechanism: http://blog.csdn.net/yuanzeyao/article/details/13630909

Android system Key event transfer mechanism: http://blog.csdn.net/yuanzeyao/article/details/13631139

 

I plan to use multiple articles to explain the content as it involves a lot of content.

Now let's start learning the measurement process of a View. The main function of the measurement process is to calculate the size of a View. This is actually easy to understand, because when any View is drawn to the UI, you must know the size of the View beforehand. Otherwise, the View cannot be drawn.

We usually specify the size of a view. The layout_width and layout_hegiht attributes are usually set in the xml file. Here I want to raise a question: why is layout in front of the attribute name corresponding to the width and height of the View, instead of width and height? Remember this question first. After reading the content in this article, I believe you will understand it. In fact, the measurement process changes the layout_width and layout_height attributes to specific numbers.

 

When we want to display an xml file to the UI, we usually pass the id of the xml file to the setContentView of the Activity. In fact, we will eventually call the javasmtraversals method of ViewRoot, this method is used to draw the View of Android. This method has a lot of code, but the logic is very simple, mainly including three stages:

The first stage is the measure we want to learn today. The second stage is layout, the third stage is draw, and the measure stage is to get the size of each View, the layout stage is to calculate the coordinates of each View on the UI. The draw stage is to draw the UI based on the data of the previous two stages.

 

First, let's take a look at part of the code of ViewRoot's javasmtraversals method (the reason why code 2.3 is selected is that Version 2.3 logic is simpler than version 4.x, and the main logic is the same)

 

Private void initialize mtraversals () {// Section one mView is DecorView, final View host = mView; // Section two int desired1_wwidth; int desired1_wheight; int childWidthMeasureSpec; int timeout ;... rect frame = mWinFrame; if (mFirst) {fullRedrawNeeded = true; mLayoutRequested = true; DisplayMetrics packageMetrics = mView. getContext (). getResources (). getDisplayMetrics (); // Section thr Ee desired1_wwidth = packageMetrics. widthPixels; desired1_wheight = packageMetrics. heightPixels; // For the very first time, tell the view hierarchy that it // is attached to the window. note that at this point the surface // object is not initialized to its backing store, but soon it // will be (assuming the window is visible )....} else {// Section four desired1_wwidth = frame. width (); Desired1_wheight = frame. height (); if (desired1_wwidth! = MWidth | desired1_wheight! = MHeight) {if (DEBUG_ORIENTATION) Log. v (ViewRoot, View + host + resized to: + frame); fullRedrawNeeded = true; mLayoutRequested = true; windowResizesToFitContent = true ;}} boolean insetsChanged = false; if (mLayoutRequested) {// Execute enqueued actions on every layout in case a view that was detached // enqueued an action after being detached getrunqueue(cmd.exe cuteActions (attachInfo. mHandler );... // Section five childWidthMeasureSpec = getRootMeasureSpec (desired1_wwidth, lp. width); childHeightMeasureSpec = getRootMeasureSpec (desired1_wheight, lp. height); // Ask host how big it wants to be if (DEBUG_ORIENTATION | DEBUG_LAYOUT) Log. v (ViewRoot, Measuring + host + in display + desired1_wwidth + x + desired1_wheight + ...); // Section six host. measure (childWidthMeasureSpec, childHeightMeasureSpec); if (DBG) {System. out. println (============================================ ); system. out. println (maid -- after measure); host. debug ();}}....}

 

The above code is the main code of the first stage. Please refer to Section one in the Code. Here, a View type variable host is defined, which is assigned a value to mView, here I want to talk about mView as a DecorView on the interface. If you are not familiar with DecorView, you can read my other article:

In the window creation process, Section two defines four int-type variables. The first two variables are assigned values in Section three or Section four, usually, the first time we come in is to assign values in Section three, that is, desired1_wwidth and disire?wheight are respectively the width and height of the mobile phone screen (of course not always like this. Here we only need to consider a simple situation ), in Section five, assign values to childWidthMeasureSpec and childHeightMeasureSpec respectively. Here, we call the getRootMeasureSpec method and analyze it later. In Setion six, host is called. measure is used to calculate the View Size. Here, the call process of mersure in javasmtraversals is over, but we still don't know what the getRootMeasureSpec and host measure methods have done, the following two methods are analyzed:

Let's take a look at the getRootMeasureSpec method.

 

    private int getRootMeasureSpec(int windowSize, int rootDimension) {        int measureSpec;        switch (rootDimension) {        case ViewGroup.LayoutParams.MATCH_PARENT:            // Window can't resize. Force root view to be windowSize.            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);            break;        case ViewGroup.LayoutParams.WRAP_CONTENT:            // Window can resize. Set max size for root view.            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);            break;        default:            // Window wants to be an exact size. Force root view to be that size.            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);            break;        }        return measureSpec;    }

After reading the implementation, do you think the implementation of this method is extremely simple, using getRootMeasureSpec (desired1_wwidth, lp. width) for example, we know that the first parameter is the screen width, and the second parameter is the width attribute in LayoutParams of a View. In fact, this parameter is in the Activity

 

 void makeVisible() {        if (!mWindowAdded) {            ViewManager wm = getWindowManager();            wm.addView(mDecor, getWindow().getAttributes());            mWindowAdded = true;        }        mDecor.setVisibility(View.VISIBLE);    }

The makeVisible method is passed in. makeVisible is called in the onResume of the Activity. We do not care about this first. We are concerned about how this lp is created. Let's take a look at getWindow. what does getAttributes () do?

 

 

  // The current window attributes.    private final WindowManager.LayoutParams mWindowAttributes =        new WindowManager.LayoutParams();

Find the getAttributes method of Window through the source code. This method returns the value of msf-wattributes. Let's look at the empty constructor of the WindowManager. LayoutParams class.

 

 

           public LayoutParams() {            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);            type = TYPE_APPLICATION;            format = PixelFormat.OPAQUE;        }

After reading the constructor, we found that both layout_width and laout_height are MATCH_PARENT. For the lp parameter, we can see it here first. Let's continue to look at the getRootMeasureSpec method,

 

Here is a strange MeasureSpec class. First, let's see where MeasureSpec is. MeasureSpec is an internal class defined in the View. There are several important constants in this class:

 

 

       private static final int MODE_SHIFT = 30;        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;        /**         * Measure specification mode: The parent has not imposed any constraint         * on the child. It can be whatever size it wants.         */        public static final int UNSPECIFIED = 0 << MODE_SHIFT;        /**         * Measure specification mode: The parent has determined an exact size         * for the child. The child is going to be given those bounds regardless         * of how big it wants to be.         */        public static final int EXACTLY     = 1 << MODE_SHIFT;        /**         * Measure specification mode: The child can be as large as it wants up         * to the specified size.         */        public static final int AT_MOST     = 2 << MODE_SHIFT;

 

We know that the int type in java occupies 32 bits. The representation of these variables in the memory is as follows:

MODE_MASK: 11000000 00000000 00000000 00000000

UNSPECIFIED: 000000000 00000000 00000000 00000000

EXACTLY: 01000000 00000000 00000000 00000000

AT_MOST: 10000000 00000000 00000000 00000000

That is to say, for each model with two digits in height, 30th digits actually represent the size of the dimension.

 

With the above foundation, I believe it is not difficult to understand the following three methods.

 

/**         * Creates a measure specification based on the supplied size and mode.         *         * The mode must always be one of the following:         * 
  • *
  • {@ Link android. view. View. MeasureSpec # UNSPECIFIED}
  • *
  • {@ Link android. view. View. MeasureSpec # EXACTLY}
  • *
  • {@ Link android. view. View. MeasureSpec # AT_MOST}
  • *
** @ Param size the size of the measure specification * @ param mode the mode of the measure specification * @ return the measure specification based on size and mode */public static int makeMeasureSpec (int size, int mode) {return size + mode;}/*** Extracts the mode from the supplied measure specification. ** @ param measureSpec the measure specification to extract the mode from * @ return {@ link Android. view. view. measureSpec # UNSPECIFIED}, * {@ link android. view. view. measureSpec # AT_MOST} or * {@ link android. view. view. measureSpec # EXACTLY} */public static int getMode (int measureSpec) {return (measureSpec & MODE_MASK);}/*** Extracts the size from the supplied measure specification. ** @ param measureSpec the measure specification to extract the size from * @ return the size in pixels defi Ned in the supplied measure specification */public static int getSize (int measureSpec) {return (measureSpec &~ MODE_MASK );}
The first method makeMeasureSpec is to add the size and mode to return the result. The second getMode is to get the value of the higher two bits, and getSize is to get the value of the lower 30 bits.

 

 

After reading this, let's go back to getRootMeasureSpec. We know lp. there are usually three types of width attributes: match_parent (fill_parent) and wrap_content. The specific size (such as 100dip) is shown in the preceding analysis. The width and height are match_parent. Through the code, we know that the modes corresponding to these three conditions are:

EXACTLY, AT_MOST, EXACTLY, that is, math_parent corresponds to the specific size (100dip) of EXACTLY. Finally, call the makeMeasureSpec Method Based on the obtained mode and screen width to obtain an int value and assign it to childWidthMeasureSpec. Similarly, childHeightMeasureSpec is obtained and these two values are passed into measure. Next let's take a look at what measure has done.

 

Because the measurement of the host is called here, and the host is actually a FrameLayout, I do not intend to continue using this example to measure the View, but the ViewGroup does not change the measurement, therefore, the measure method of View is actually called. The source code of the measure method is as follows:

 

 

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {        if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||                widthMeasureSpec != mOldWidthMeasureSpec ||                heightMeasureSpec != mOldHeightMeasureSpec) {            // first clears the measured dimension flag            mPrivateFlags &= ~MEASURED_DIMENSION_SET;            if (ViewDebug.TRACE_HIERARCHY) {                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);            }            // measure ourselves, this should set the measured dimension flag back            onMeasure(widthMeasureSpec, heightMeasureSpec);            // flag not set, setMeasuredDimension() was not invoked, we raise            // an exception to warn the developer            if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {                throw new IllegalStateException(onMeasure() did not set the                        +  measured dimension by calling                        +  setMeasuredDimension());            }            mPrivateFlags |= LAYOUT_REQUIRED;        }        mOldWidthMeasureSpec = widthMeasureSpec;        mOldHeightMeasureSpec = heightMeasureSpec;    }

We can see that the measure method is actually final, so ViewGroup cannot rewrite this method. Generally, a specific ViewGroup is used to rewrite the onMeasure method. You can check the LinearLayout and FrameLayout methods. They indirectly call the measureChildWithMargins method of ViewGroup in the onMeasure method, today, we will take the measureChildWithMargins method as the entrance to analyze the measurement process of the View. The source code of the measureChildWithMargins method is as follows:

 

    protected void measureChildWithMargins(View child,            int parentWidthMeasureSpec, int widthUsed,            int parentHeightMeasureSpec, int heightUsed) {        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin                        + widthUsed, lp.width);        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin                        + heightUsed, lp.height);        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);    }
Here we can simplify the case. We assume that all the children in the ViewGroup are views without ViewGroup.

 

Next we will analyze the measureChildWithMargins method in three steps:

1. Get the child's LayoutParams

2. Call the getChildMeasureSpec method to obtain the child's measureSpec (including widthSpec and heightSpec)

Let's take a look at what getChildMeasureSpec has done. Let's take a look at several of its parameters to get the child's widthSpec. The first parameter is the widthSpec of ViewGroup, and the second parameter is the width used by ViewGroup, the third is lp. width, Next let's look at the source code:

 

   */    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {        int specMode = MeasureSpec.getMode(spec);        int specSize = MeasureSpec.getSize(spec);        int size = Math.max(0, specSize - padding);        int resultSize = 0;        int resultMode = 0;        switch (specMode) {        // Parent has imposed an exact size on us        case MeasureSpec.EXACTLY:            if (childDimension >= 0) {                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size. So be it.                resultSize = size;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size. It can't be                // bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            }            break;        // Parent has imposed a maximum size on us        case MeasureSpec.AT_MOST:            if (childDimension >= 0) {                // Child wants a specific size... so be it                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size, but our size is not fixed.                // Constrain child to not be bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size. It can't be                // bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            }            break;        // Parent asked to see how big we want to be        case MeasureSpec.UNSPECIFIED:            if (childDimension >= 0) {                // Child wants a specific size... let him have it                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size... find out how big it should                // be                resultSize = 0;                resultMode = MeasureSpec.UNSPECIFIED;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size.... find out how                // big it should be                resultSize = 0;                resultMode = MeasureSpec.UNSPECIFIED;            }            break;        }        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);    }

I believe that with the previous foundation, it is very easy to look at this Code. In fact, it is based on the ViewGroup mode, size, and lp. width values to create the measureSpec of the View. Do you know the answer to the question I mentioned earlier? Why should I add a layout before the width, because the size of the Child View itself (Child View) and ViewGroup (parent View) jointly determined.

 

 

Return to measureChildWithMargins and check the third step: Call child. measure. And the parameter is obtained in step 2. Note that this child is a normal View (because we have assumed that there is no ViewGroup in the ViewGroup, only View)

 

 

Because a View calls measure, calling onMeasure in measure is also in View. Let's take a look at the onMeasuere method of View.

 

 

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }

 

An important method, getDefaultSize, is displayed. The Code is as follows:

 

    public static int getDefaultSize(int size, int measureSpec) {        int result = size;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize =  MeasureSpec.getSize(measureSpec);        switch (specMode) {        case MeasureSpec.UNSPECIFIED:            result = size;            break;        case MeasureSpec.AT_MOST:        case MeasureSpec.EXACTLY:            result = specSize;            break;        }        return result;    }

This method determines whether the returned value is size or specSize based on the mode of measureSpec. In most cases, the download mode is AT_MOST or EXACTLY. (UNSPECIFIED usually appears when we call this view to obtain a view in an hour. displayed when measure .), in onMeasure, The setMeasuredDimension () method is called to assign the obtained values to mMeasuredWidth and mMeasuredHeight respectively, so that the View size is measured.

 

The Code is as follows:

 

  protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {        mMeasuredWidth = measuredWidth;        mMeasuredHeight = measuredHeight;        mPrivateFlags |= MEASURED_DIMENSION_SET;    }

The measurement process of View has come to an end here. As for the measurement process of ViewGroup, use LinearLayout in the next article.

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.