Introduction:
This chapter mainly introduces the working principle of View, and Android custom controls can be compared to see.
The main content
- View wroot and DecorView for the first time
- Understand the MeasureSpec
- View workflow
- The custom View
The specific content
View wroot and DecorView for the first time
The implementation of ViewRoot is the ViewRootImpl class, which is the link between Windows Manager and DecorView. Measure, Layout, and draw of View are all done through ViewRoot. When the Activity object is created, add the DecorView to the Window, create a ViewRootImpl object, and connect the ViewRootImpl object to the DecorView.
root = new ViewRootImpl(view.getContext(),display);
root.setView(view, params, panelParentView);
Copy the code
The rendering of a View begins with the FormTraversals of the View:
- Measure measures the width and height of a View
- Layout determines the position of the View in the parent container
- Draw is responsible for drawing the View on the screen
PerformTraversals invoke performMeasure, performLayout, and performDraw, which performMeasure, layout, and draw of top-level views, respectively. Among them, the measure method will be called in performMeasure, and the onMeasure method will be called in the measure method. In the onMeasure method, the measure process will be performed on all the child elements, thus completing a measure process. The child element repeats the parent’s measure process, iterating through the View count. Same thing for the other two.
- Once the Measure is complete, the measureDWidth and measureHeight methods getMeasuredWidth and getMeasureHeight can be used to obtain the measuredWidth/measureHeight of the View. In special cases, the measured width is not equal to the final width, see below.
- The Layout process determines the coordinates of the View’s four vertices and the width and height of the actual View. After completion, you can get the View’s four fixed points through getTop, getBottom, getLeft and getRight.
DecorView is a FrameLayout that contains a vertical LinearLayout divided into the title bar and the interior bar.
<div align="center">
<img src="http://images2015.cnblogs.com/blog/500720/201609/500720-20160925174505236-1295369287.png" width = "150" height = "200" alt="Image" align=center />
</div>
Copy the code
The layout file that is set in the Activity via setContextView is actually loaded into the interior bar. Content = findViewById(r.android.id.content); You get this contentView. View layer events are passed through the DecorView before being passed to child Views.
Understand the MeasureSpec
MeasureSpec determines the size of a View. But the parent container affects the creation of the View’s MeasureSpec. The system converts the View’s LayoutParams to a MeasureSpec according to the rules imposed by the parent container, and then measures the View’s width and height according to this MeasureSpec.
MeasureSpec
MeasureSpec represents a 32-bit INT value, with the higher 2 bits representing SpecMode and the lower 30 bits representing SpecSize for a measurement mode.
There are three types of SpecMode:
- UNSPECIFIED: The parent container has no restrictions on the View, as UNSPECIFIED, and is generally used internally.
- EXACTLY: When the parent container detects the exact size required by the View, the final size of the View is the value specified by SpecSize, corresponding to the match_parent and specific value modes in LayoutParams.
- AT_MOST: the default size of the View. Different views have different implementations. The size of the View cannot be larger than the SpecSize of the parent container, which corresponds to wrAP_content in LayoutParams.
MeasureSpec and LayoutParams
For a DecorView, its MeasureSpec is determined by the size of the window and its own LayoutParams. A View’s MeasureSpec is determined by both the parent container’s MeasureSpec and its own LayoutParams.
The Measure procedure of a View is passed from the ViewGroup to the measureChildWithMargins method. To measure the child’s MeasureSpec, refer to the measureChildWithMargins method of the ViewGroup. Call the measure method of the child element.
- ParentSize refers to the size currently available in the parent container.
When a View has a fixed width/height (that is, set a fixed DP /px), the View’s MeasureSpec is EXACTLY mode regardless of the parent container’s MeasureSpec, and the size follows the value we set.
- When a View’s width/height is match_parent, a View’s MeasureSpec is EXACTLY mode and is equal to the remaining space of the parent container.
- When a View’s width/height is WRAP_content, the View’s MeasureSpec is AT_MOST and cannot exceed the remaining space of the parent container.
- The UNSPECIFIED mode of the parent container is used for multiple measures within the system and represents the state of the measurement. Generally, this mode is not worth paying attention to.
View workflow
Measure the process
View’s measure process: Custom controls that directly inherit the View need to override the onMeasure method and set the size of wrap_content. Otherwise, using wrap_content in the layout is equivalent to using match_parent. In the case of non-WRAP_content, we just use the system measurements.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
// In MeasureSpec.AT_MOST, give a default value of mWidth,mHeight. The default width and height are flexibly specified
// See TextView and ImageView
// In other cases, follow the system measurement rules
if (widthSpecMode == MeasureSpec.AT_MOST
&& heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWith, mHeight);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWith, heightSpecSize);
} else if(heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpecSize, mHeight); }}Copy the code
Measure procedure for ViewGroup: The ViewGroup is an abstract class that does not override the View’s onMeasure method, but provides a measureChildren method. This is because different ViewGroup subclasses have different layout properties, resulting in different measurement details, such as LinearLayout and RelativeLayout. Therefore, viewgroups cannot implement onMeasure methods at the same time.
The measureChildren method process:
- Pull out the child View’s LayoutParams.
- Create child elements’ MeasureSpec using the getChildMeasureSpec method.
- MeasureSpec is passed directly to the View’s measure method to make the measurement.
The onMeasure method on the LinearLayout is used to analyze the ViewGroup’s measure process:
- The LinearLayout uses match_parent or specific value in the layout, and the measurement process is consistent with View, that is, the height is specSize.
- The LinearLayout uses wrAP_content in the layout, then its height is the sum of the heights taken up by all its children, but not more than the remaining space of its parent container.
- The final height of the LinearLayout also takes into account the vertical padding.
The measure process of a View is the most complex of the three processes. Once the measure is completed, the measuredWidth /Height method can be used to obtain the measuredWidth /Height of the View. In some cases, the system may need multiple measures to determine the final measurement width/height, so the width/height obtained in onMeasure may not be accurate.
What if we want to get the width and height of a View when our Activity starts? Since the View’s measure process and the Activity’s life cycle are not executed synchronously, there is no guarantee that a View has been measured when the Activity’s onCreate, onStart, and onResume are completed. So there are four ways to get the View’s width and height:
- Activity/View#onWindowFocusChanged
The onWindowFocusChanged method means that the VieW is initialized and the width and height are ready. Note that it will be called multiple times, both when the Activity window gains focus and when it loses focus. 2. View.post (Runnable) Post a runnable to the end of the message queue. When Looper calls the runnable, the view is initialized. This is done using the ViewTreeObserver’s many callbacks, such as the OnGlobalLayoutListener interface, when the state of the View tree changes or the visibility of the View inside the View tree changes. The onGlobalLayout method is called back, which is a good time to get the View width and height. Note that onGlobalLayout will be called back multiple times as the state of the View tree changes. 4. View. Measure (int widthMeasureSpec,int heightMeasureSpec) Manually measure the view. View layoutParams: View layoutParams
- Match_parent: cannot measure the specific width and height, because it cannot measure the size of the View without knowing the remaining space of the parent container.
- Specific values (dp/px) :
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(100,MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(100,MeasureSpec.EXACTLY);
view.measure(widthMeasureSpec,heightMeasureSpec);
Copy the code
- Wrap_content:
int widthMeasureSpec = MeasureSpec.makeMeasureSpec((1<<30) -1,MeasureSpec.AT_MOST);
// The size of a View is expressed as a 30-bit binary with a maximum of 30 1s. In AT_MOST mode, it is reasonable to construct a MeasureSpec with the maximum size that the View can theoretically support
int heightMeasureSpec = MeasureSpec.makeMeasureSpec((1<<30) -1,MeasureSpec.AT_MOST);
view.measure(widthMeasureSpec,heightMeasureSpec);
Copy the code
The layout process
The ViewGroup is used to determine the position of the child View. When the position of the ViewGroup is determined, it will traverse all the child views in onLayout and call its Layout method. In the Layout method, the onLayout method will be called.
View layout method to determine its position, the source process is as follows:
- SetFrame determines the position of the View’s four vertices, that is, the position of the View in its parent container.
- Call onLayout method to determine the location of all child views. Just like onMeasure, the specific implementation of onLayout is related to the layout, so the View and ViewGroup do not really implement onLayout method.
Take the onLayout method on the LinearLayout as an example:
- Iterates through all child views and calls the setChildFrame method to specify the location of the child elements.
- The setChildFrame method actually calls the layout method of the child View, creating recursion.
The difference between the measured height and final height of a View: In the default implementation of a View, the measured height and final height of a View are the same, except that the measured height is formed in the measure process and the final width is formed in the Layout process. But rewriting the Layout method of the View can make them unequal.
The draw process
The process of drawing a View follows the following steps:
- Draw the background drawBackground(Canvas).
- Draw yourself onDraw.
- Draw children dispatchDraw to traverse the draw method of all child Views.
- Draw decorated onDrawScrollBars.
SetWillNotDraw is enabled by default, so the system will not execute onDraw, so custom ViewGroup needs to draw content through onDraw, must explicitly turn off the optimization flag WILL_NOT_DRAW. Call setWillNotDraw(false).
The custom View
Custom View classification
Inherit View rewrite onDraw method: through the onDraw method to achieve some irregular effect, this effect is not convenient to achieve through the combination of layout. This method needs to support wrap_content itself, and the padding needs to be handled as well.
Inherit ViewGroup to derive special layout: to achieve a custom layout, you need to properly handle the measurement and layout of the ViewGroup, and process the measurement and layout of sub-views.
Inheriting specific View subclasses (such as TextView, Button) : It’s easy to extend the functionality of an existing control without having to manage wrap_content and padding yourself.
Inheriting a specific ViewGroup subclass (such as LinearLayout) : Relatively common, to achieve a combination of several views. The difference with Method 2 is that method 2 is closer to the underlying implementation.
Customize View notes
- Controls that directly inherit from a View or ViewGroup require special processing of wrAP_content in onMeasure. Specifies the default width/height in wrAP_content mode.
- Control that directly inherits from the View, the padding property does not work if the padding is not handled in the draw method. Controls that directly inherit from ViewGroup also need to take the padding and child element margin into account in onMeasure and onLayout. Otherwise, the padding and child element margin are invalid.
- Try not to use handlers in views because you don’t need them. View internal provides a post series of methods, can completely replace the role of the Handler.
- The View has threads and animations that need to be stopped in the View’s onDetachedFromWindow. Threads and animations also need to be stopped when the View is not visible, otherwise memory leaks can occur.
- Sliding conflicts need to be handled when a View has sliding nesting.
Custom View instances
- Inherit View to override onDraw: CircleView
- Inherit from ViewGroup to derive a special layout: HorizontalScrollViewEx
In the onMeasure method, first determine whether there are child elements, if not, perform corresponding processing according to the width and height of LayoutParams. Then determine if the width is wrap_content, and if it is, then the width of the HorizontalScrollViewEx is the sum of the widths of all the child elements. If the height is wrap_content, the height of the HorizontalScrollViewEx is the height of the first child element. You have to handle both the padding and the margin. The onLayout method also considers padding and margin when placing child elements.
The idea of customizing views
- Master basic skills, such as elastic sliding of View, sliding conflict, drawing principle, etc
- When facing a new custom View, classify it and select the appropriate implementation ideas.