1. The drawing process of view is divided into several steps. Where do you start? After which process can I see the view?
Starting from the performTraversals of ViewRoot, it goes through three processes: measure, layout and draw. After the draw process is completed, you can see the view on the screen.
2. Is there any difference between the measured width and height of view and the actual width and height?
Basically, 99% of the cases can be considered as no difference. There are two cases, there are differences. The first is that sometimes the width and height measured for the first time are not necessarily equal to the actual width and height, but in this case
The last measured width and height are consistent with the actual width and height. In addition, the actual width and height are determined in the layout process. We can write the actual width and height into hard code in the layout process, so the measured width and height must be different from the actual width and height, although it is meaningless and bad.
3. Who decides the measureSpec of view? What about the top view?
The view's own layoutparams and parent container together determine its own measureSpec. Once the spec is determined, the width and height of the view can be determined in onMeasure.
The top-level view is a little special. The measurement of decorView is in the source code of ViewRootImpl.
//These two parameters of desire represent the width and height of the screen, childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //The measurespec of decorView is determined here. In fact, it is much simpler than the measurespec of ordinary view //The code doesn't analyze things at a glance private static 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; }
4. For an ordinary view, is the measure ment process related to the parent view? If so, what role does the parent view, that is, viewgroup, play?
Look at the source code:
//For the measure of an ordinary view, it is triggered by the parent view of the view, that is, viewgroup. //This is the measureChildWithMargins method below protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { //The first step is to obtain the layoutParams parameter value of the child view final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //Then start to calculate the spec value of the sub view. Note that the layoutparams parameter of the sub view is used for calculation //It also uses the value of the parent view, that is, the spec of the viewgroup itself 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); } //The method of calculating the spec of view looks like a large string, but the logic is very simple, which is based on the father viewgroup //The meaurespec of the view also has its own params to determine its own measureSpec. //Note that the parameter here is padding, which means the size of the control occupied by the parent container, so the Specsize of the view //You can see that you want to subtract the padding value. Total size - used = available. Well understood. //Then the following switch logic should be sorted out by yourself. In fact, it is not difficult, mainly the following principles //If the view adopts a fixed width and height, that is, the value written dead. Regardless of the value of the father's spec, the spec of the view must be exactly, and the size follows the size set in the layout parameter. //If the width and height of the view is match_parent, it depends on the spec value of the parent container viewgroup. If the spec of the parent view is in the exact mode, //The view must be exactly, and the size is the remaining space of the parent container. If the parent container is at_ In most mode, the view is also at_most and will not exceed the remaining space size //If the width and height of the view is wrap_content, regardless of the spec of the parent container, the spec of view must be at_most and will not exceed the size of the remaining space of the parent view. 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 = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
5. What is the relationship between measure in view and onMeasure?
Look at the source code:
//The measure of view is the final method, which cannot be modified by our subclass. public final void measure(int widthMeasureSpec, int heightMeasureSpec) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int oWidth = insets.left + insets.right; int oHeight = insets.top + insets.bottom; widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth); heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); } // Suppress sign extension for the low bytes long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2); if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded(); int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { throw new IllegalStateException("View with id " + getId() + ": " + getClass().getName() + "#onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension } //However, you can see that the onMeasure method is called in the measure method //So we can know that we must rewrite this method when customizing the view! protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
6. Briefly analyze the measure process of view?
First review question 4. After the viewgroup calculates the spec of the sub view, it will call the measure method of the sub view, and we also saw the onMeasure method actually called in question 5
Therefore, we only need to analyze the onMeasure method. Note that the parameters of the onMeasure method are the values of the two specs calculated by its parent view (here, the measure method of view will slightly modify the specSize value in this spec. this part is not analyzed because the measure method is very simple to modify the specSize part).
//It can be seen that this is the call of the setMeasuredDimension method. This method can be known by its name to determine the measured width and height of the view //Therefore, the focus of our analysis is to see how the getDefaultSize method determines the measured width and height of the view protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } //This method is particularly simple. It can basically be regarded as an approximate return of the specSize in the spec, unless your specMode is UNSPECIFIED //UNSPECIFIED is generally used for internal measurement of the system. In this case, the returned size is the return value of getSuggestedMinimumWidth 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; } //It's related to the background of view. There's no more analysis here protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }
7. In the custom view, if the onMeasure method does not wrap_ What happens when content is processed? Why? How?
If there is no wrap_content is processed, even if you set wrap in xml_ Content. Its effect is also similar to match_ The parent is the same. Look at the analysis of question 4. We can know that the layout of the view is wrap, and the mode is at_most (no matter what specmode the father view is)
In this mode, the width and height are equal to specSize (according to the analysis of getdefaultsize function), and the specSize here is obviously the size of parentSize. That is, the remaining size of the parent container. Then set it directly to match with us_ Is parent the same effect?
The solution is to do special processing for wrap in onMeasure, such as specifying a default width and height. When it is found to be wrap_content just set the default width and height.
8. Does ViewGroup have onMeasure method? Why?
No, this method is implemented by the subclass itself. The layout of different viewgroup subclasses must be different, so onMeasure can simply be implemented by themselves.
9. Why can't the measured width and height be obtained in the life cycle of the activity? Is there any way to solve this problem?
Because the process of measure has nothing to do with the life cycle of activity. You can't be sure which life cycle is completed. The measure process of view must be completed. You can try the following methods to obtain the measured width and height of the view.
//Override this method of the activity public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { int width = tv.getMeasuredWidth(); int height = tv.getMeasuredHeight(); Log.v("burning", "width==" + width); Log.v("burning", "height==" + height); } }
perhaps
@Override protected void onStart() { super.onStart(); tv.post(new Runnable() { @Override public void run() { int width = tv.getMeasuredWidth(); int height = tv.getMeasuredHeight(); } }); }
perhaps
@Override protected void onStart() { super.onStart(); ViewTreeObserver observer = tv.getViewTreeObserver(); observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int width = tv.getMeasuredWidth(); int height = tv.getMeasuredHeight(); tv.getViewTreeObserver().removeOnGlobalLayoutListener(this); } }); }
10. What is the difference between layout and onLayout methods?
Layout determines the position of the view itself, while onLayout determines the position of all child elements. In layout, the positions of the four vertices of the view are set through the serFrame method. These four positions are fixed to determine their own position
Onlayout is then called to determine the location of the child elements. The onlayout methods of view and viewgroup are not written. Leave it to us to lay out the child elements
11. How many steps does the draw method take?
There are four steps in total, drawing the background ----------- drawing yourself ----------- drawing chrildren ------ drawing decoration.
12. What is the use of setwillnotdraw method?
This method is in the view.
/** * If this view doesn't do any drawing on its own, set this flag to * allow further optimizations. By default, this flag is not set on * View, but could be set on some View subclasses such as ViewGroup. * * Typically, if you override {@link #onDraw(android.graphics.Canvas)} * you should clear this flag. * * @param willNotDraw whether or not this View draw on its own */ public void setWillNotDraw(boolean willNotDraw) { setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); }
13. What should I pay attention to when customizing the view?
It mainly deals with wrap_content and padding. Otherwise, it is useless to set these two attributes in xml. Also, don't use handler in the view, because someone else has provided the post method. If it is inherited from viewGroup, it should also be considered in onMeasure and onLayout
The impact of padding and layout. In other words, the specSize needs to be calculated. Finally, if the view animation or thread needs to be stopped, you can consider doing it in onDetachedFromWindow.