Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
An overview of
1.1 Android View inheritance diagram
View is the parent class of all Android controls.
1.2 View coordinates of Android
1.2.1 Android Coordinate system
GetRawX () and getRawY() provided by MotionEvent;
1.2.2 View Coordinate system
1.2.3 Common methods
View Gets its width and height
getHeight() | Gets the height of the View itself |
---|---|
getWidth() | Gets the View’s own width |
Distance between View and its parent (ViewGroup) :
getTop() | Gets the distance from the top edge of the View itself to the top edge of its parent layout |
---|---|
getLeft() | Gets the distance from the left of the View itself to the left of its parent layout |
getRight() | Gets the distance from the right of the View itself to the left of its parent layout |
getBottom() | Gets the distance from the bottom edge of the View itself to the top edge of its parent layout |
Method provided by MotionEvent
So if we look at that dark blue dot in the figure above, let’s say that’s the point we touch, and we know that the final click event, whether it’s a View or a ViewGroup, is going to be handled by onTouchEvent, MotionEvent also provides various methods for getting focal coordinates:
getX() | Gets the distance from the click event to the left of the control, the view coordinates |
---|---|
getY() | Gets the distance of the click event from the top edge of the control, the view coordinates |
getRawX() | Gets the absolute coordinates of how far the click event is to the left of the entire screen |
getRawY() | Take the distance between the click event and the top edge of the entire screen, that is, the absolute coordinates |
2. View event distribution
Click event distribution: Clicking on the screen produces a touch event encapsulated in a class: MotionEvent. When this MotionEvent is generated, the system will send this MotionEvent to all levels of the View. The process of MotionEvent transmission at the View level is click event distribution.
There are three important ways to click on events:
dispatchTouchEvent(MotionEvent ev) | Used for event distribution |
---|---|
onInterceptTouchEvent(MotionEvent ev) | For event interception, called from dispatchTouchEvent(). Note that this method is not provided by the View |
onTouchEvent(MotionEvent ev) | Used to handle click events, called in the dispatchTouchEvent() method |
The relationship between these three important methods of click event distribution is simply expressed in pseudocode:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean result=false;
if(onInterceptTouchEvent(ev)){
result=super.onTouchEvent(ev);
}else{
result=child.dispatchTouchEvent(ev);
}
return result;
Copy the code
This U-shaped diagram is a good example of event distribution:
Summary: dispatchTouchEvent represents the dispatch event, onInterceptTouchEvent() represents the intercept event, onTouchEvent() represents the consumable event, which is handled by itself.
3. Work flow of View
3.1 an overview of the
Measure: measure the width and height of the View; Measurement;
Layout: Used to determine the position of the View; Layout;
Draw: is used to draw a View; Drawing;
3.2 measurement
MeasureSpec (important)
MeasureSpec is the static inner class of a View; MeasureSpec MeasureSpec MeasureSpec MeasureSpec MeasureSpec MeasureSpec MeasureSpec MeasureSpec
The measurement process is essentially the process of determining the View’s MeasureSpec, which contains two main parameters: mode + size.
MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec
3.2.2 SpecMode Classification of MeasureSpec
UNSPECIFIED
public static final int UNSPECIFIED = 0 << MODE_SHIFT; 00000000000000000000000000000000
The parent container does not impose any restrictions on the View, and is used internally
EXACTLY public static final int EXACTLY = 1 << MODE_SHIFT; 01000000000000000000000000000000 the parent container of measuring the size of the View, the size of the Vew is SpecSize LayoutPamras match_parent fixed size
AT_MOST public static final int AT_MOST = 2 << MODE_SHIFT; 10000000000000000000000000000000 the parent container to specify a size are available, and the size of the View must not exceed this value, LayoutPamras wrap_content
3.2.3 Mapping process
ViewGroup mapping process:
Measure –> onMeasure –>setMeasuredDimension –>setMeasuredDimensionRaw
View mapping process:
Measure –> onMeasure –>setMeasuredDimension –>setMeasuredDimensionRaw
3.2.4 Mapping source code analysis, using DecorView as an example
First in view otimPL, execute the performMeasure method:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally{ Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}Copy the code
Click the mview. measure method to enter the View’s measure method (marked 1) (the method of View is final and cannot be overridden) as follows:
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);
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
// Optimize layout by avoiding an extra EXACTLY pass when the view is
// already measured as the correct size. In API 23 and below, this
// extra pass is required to make LinearLayout re-distribute weight.
final booleanspecChanged = widthMeasureSpec ! = mOldWidthMeasureSpec || heightMeasureSpec ! = mOldHeightMeasureSpec;final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
&& getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
final booleanneedsLayout = specChanged && (sAlwaysRemeasureExactly || ! isSpecExactly || ! matchesSpecSize);if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
/ / here! The important onMeasure method finally finds you!
/ / here! The important onMeasure method finally finds you!
/ / here! The important onMeasure method finally finds you!
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
}
Copy the code
There’s a lot of code. Don’t look at it all. The first half is basically fetching the cache, and then executing the onMeasure(widthMeasureSpec, HeightMeasure) method. A setting that will be cached if the cache is not read. (The cache can be ignored for now, mainly to do some optimization for the measurement)
Next in the code snippet above, click on the main onMeasure method to check it out:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
Copy the code
The onMeasure method calls two main methods: setMeasuredDimension and getDefaultSize. Let’s start with the getDefaultSize method:
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;
}
Copy the code
Note that either AT_MOST or EXACTLY mode is the default specSize, so we must override the onMeasure method when we customize the View, otherwise match_parent and wrap_content properties will not work.
Let’s take a look at what’s in the setMeasuredDimension method:
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if(optical ! = isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets();int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
Copy the code
The setMeasuredDimension method calls the setMeasuredDimensionRaw(measuredWidth, measuredHeight) method.
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
Copy the code
Thank god it’s over! As you can see, the method assigns a value to the width and height member variable and sets a marker bit.
Don’t walk so far that you forget why you started. Measure (childWidthMeasureSpec, childHeightMeasureSpec) keeps coming back here by executing the mView. Measure (performMeasure) method in ViewRootImpl, So measure is essentially determining the width and height of the control. How do the width and height of the controls determine their values? Now we need to look at mView.measure(childWidthMeasureSpec, childHeightMeasureSpec). ChildHeightMeasureSpec these two parameters!
Here is a line for everyone to understand, above is the source code process analysis of mview.measure method.
Next we need to know how to get the two parameters of the mview.measure method.
Continue reading the source code in ViewRootlmpl:
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
+ " coveredInsetsChanged=" + contentInsetsChanged);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
Copy the code
As you can see, GetRootMeasureSpec (mWidth, lp.width) and getRootMeasureSpec(mHeight, lp.width). Lp. Height). In the first case, mWidth represents the width of the PhoneWindow, and the second argument represents the width of the top-level View itself. GetRootMeasureSpec = getRootMeasureSpec = getRootMeasureSpec = getRootMeasureSpec = getRootMeasureSpec
/**
* Figures out the measure spec for the root view in a window based on it's
* layout params.
*
* @param windowSize
* The available width or height of the window
*
* @param rootDimension
* The layout params for one dimension (width or height) of the
* window.
*
* @return The measure spec to use to measure the root view.
*/
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;
}
Copy the code
MeasureSpec (match_parent/wrAP_content) measureSpec (EXACTLY/AT_MOST) = measureSpec (EXACTLY/AT_MOST) = match_parent (EXACTLY/AT_MOST);
The DecorView’s MeasureSpec method is determined by the window size and its own LayoutParams. The specific decision rule logic in the above code can be summarized as follows:
Keep analyzing. We just determined where these two parameters came from. In fact, these two parameters determine the mode and size of the DccorView (note that the measurement of the child view has not been done yet, the process is just beginning).
Here are two arguments to the mView.measure method to avoid confusion.
Let’s move on to the mView.measure method.
Call the onMeasure method in mView.measure. We know that the DecorView actually integrates with FrameLayout, so let’s go directly to FrameLayout and see the implementation of the onMeasure method in action:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount();
final booleanmeasureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) ! = MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) ! = MeasureSpec.EXACTLY; mMatchParentChildren.clear();int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if(mMeasureAllChildren || child.getVisibility() ! = GONE) { measureChildWithMargins(child, widthMeasureSpec,0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if(lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); }}}}// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if(drawable ! =null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0. getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( width, MeasureSpec.EXACTLY); }else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0. getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( height, MeasureSpec.EXACTLY); }else{ childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }}}Copy the code
FrameLayout, as a container, must contain many child views. As you can see from the above source code method, it also traverses all its child views. It is not difficult to imagine that it will measure all its child views. To verify the conjecture, click on MeasureChildWithNavigation to find out more:
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);
}
Copy the code
In this method, the padding is added to the processing, which is easy to understand without further elaboration.
Look at the child.measure(childWidthMeasureSpec, ChildHeightMeasure) method. As you can see from the name, the width and height of the child View in FrameLayout is measured.
Here’s a very important point: the getChildMeasureSpec method, which is a child View’s MeasureSpec. Click to see the detailed implementation of the getChildMeasureSpec method:
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;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
Copy the code
This method is a bit long, so don’t be afraid. As you can see, the first argument to this method represents the parent’s MeasureSpec value (the parent’s mode and size), the second argument, the padding, and the third argument represent the size of the view itself. The method determines the mode and size of the child View by judging the mode of the parent container. The specific judgment logic can be seen from the above code, which is not detailed and summarized as follows:
After all the child controls are measured, FrameLayout starts to finalize its width and height. Continue looking at the onMeasure method in FrameLayout, and look at the second half of it. I’ll paste the key code again:
// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if(drawable ! =null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0. getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( width, MeasureSpec.EXACTLY); }else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0. getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( height, MeasureSpec.EXACTLY); }else{ childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }}Copy the code
As you can see, FrameLayout finds the height of the bottommost child and the height of the rightmost child of all child controls. From this we finally determined the width and height of Framelayout!
3.3 the layout
Source code always looks laborious, first summarized as follows:
The View:
Call the layout to determine their own position, namely to determine mLeft, mTop, mRight, mBottom value, obtain the location of the four points.
ViewGroup:
Call view. layout to determine its position, and call onLayout to determine the position of the child views.
3.3.1 Layout source code analysis, take FrameLayout as an example
View FrameLayout’s onLayout as follows:
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
Copy the code
Click to view the layoutChildren method:
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if(child.getVisibility() ! = GONE) {final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if(! forceLeftGravity) { childLeft = parentRight - width - lp.rightMargin;break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default: childTop = parentTop + lp.topMargin; } child.layout(childLeft, childTop, childLeft + width, childTop + height); }}}Copy the code
As you can see, the layoutChildren method iterates through the child View and positions the child controls. And the placement of the child control, the call is the child control layout method, that is, the last line of the code. The layout is that simple.
3.4 draw
ViewGroup drawing steps:
DrawBackground (Canvas)
Draw yourself onDraw(Canvas)
Draw sub-view dispatchDraw(Canvas)
Paint foreground, scroll bar, etc. onDrawForeground(canvas)
View drawing steps:
DrawBackground (Canvas)
Draw yourself onDraw(Canvas)
Paint foreground, scroll bar, etc. onDrawForeground(canvas)
Steps to draw a custom control:
OnMeasure –> onLayout –> onMeasure –> onLayout –> onMeasure –> onLayout –> onMeasure –> onLayout
3.4.1 Draw source code analysis, taking FrameLayout as an example
Find the performDraw method in ViewRootlmpl:
private void performDraw(a) {
if(mAttachInfo.mDisplayState == Display.STATE_OFF && ! mReportNextDraw) {return;
} else if (mView == null) {
return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
boolean usingAsyncReport = false;
if(mReportNextDraw && mAttachInfo.mThreadedRenderer ! =null
&& mAttachInfo.mThreadedRenderer.isEnabled()) {
usingAsyncReport = true;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
// TODO: Use the frame number
pendingDrawFinished();
});
}
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if(usingAsyncReport && ! canUseAsync) { mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false; }}finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
// For whatever reason we didn't create a HardwareRenderer, end any
// hardware animations that are now dangling
if(mAttachInfo.mPendingAnimatingRenderNodes ! =null) {
final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
}
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
if (mReportNextDraw) {
mReportNextDraw = false;
// if we're using multi-thread renderer, wait for the window frame draws
if(mWindowDrawCountDown ! =null) {
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
Log.e(mTag, "Window redraw count down interrupted!");
}
mWindowDrawCountDown = null;
}
if(mAttachInfo.mThreadedRenderer ! =null) {
mAttachInfo.mThreadedRenderer.setStopped(mStopped);
}
if (LOCAL_LOGV) {
Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
if(mSurfaceHolder ! =null && mSurface.isValid()) {
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
} else if(! usingAsyncReport) {if(mAttachInfo.mThreadedRenderer ! =null) { mAttachInfo.mThreadedRenderer.fence(); } pendingDrawFinished(); }}}Copy the code
Code a lot don’t have to look, find the draw method, points in the draw () method in the code is very much also, not paste, find the draw () method in drawSoftware method, and then is the mView drawSoftware method. The draw () method. Click to enter the View’s draw() method as follows:
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null| |! mAttachInfo.mIgnoreDirtyState); mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;/* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */
// Step 1, draw the background, if needed
int saveCount;
if(! dirtyOpaque) { drawBackground(canvas); }// skip step 2 & 5 if possible (common case). .Copy the code
As you can see, the comments are written in great detail, listing all the steps to draw. The omitted and unpasted code is the specific code logic for executing the steps step by step.
Finally, a god diagram:
Refer to the article
The View of the smooth movement: blog.csdn.net/itachi85/ar…
Android View system: blog.csdn.net/itachi85/ar…
Juejin. Im /post/5d3e52…
The View of workflow: blog.csdn.net/qian520ao/a…