Android hands-on teaches you how to customize ViewGroup (1)
Recently, due to changes in work, the blog update plan has been disrupted. I hope that the blog update plan will come back as soon as possible ~
Today I will give you a tutorial on custom ViewGroup. To put it bluntly, I will teach you how to customize ViewGroup. If you are not familiar with custom ViewGroup, or want to learn how to customize it, so you can take a good look at this blog.
1. Overview
Before writing code, I have to ask a few questions:
1. What is the role of ViewGroup?
ViewGroup is equivalent to a container for storing the View. When writing the layout xml, we will tell the container (all attributes starting with layout are used to tell the container ), our width (layout_width), height (layout_height), alignment (layout_gravity), and so on; of course there are margin, etc. Thus, ViewGroup functions are: calculate the recommended width and height for childView and the measurement mode. It determines the position of childView. Why is it only the suggested width and height, rather than the direct determination? Don't forget that the childView width and height can be set to wrap_content, in this way, only childView can calculate its own width and height.
2. What is the role of View?
The role of View is to calculate its own width and height based on the Measurement Mode and the width and height of the ViewGroup suggestions. In addition, it is more important: draw your own form in the area specified by ViewGroup.
3. What is the relationship between ViewGroup and LayoutParams?
You can recall that when you write the childView in LinearLayout, you can write the layout_gravity and layout_weight attributes. The childView in RelativeLayout has the limit attribute, but does not have the layout_gravity or layout_weight. Why? This is because each ViewGroup needs to specify a LayoutParams to determine the attributes supported by childView, such as LinearLayout specifying LinearLayout. LayoutParams. If you look at the source code of LinearLayout, you will find that LinearLayout. LayoutParams is defined internally. In this example, you can find weight and gravity.
2. Three measurement modes of View
As mentioned above, ViewGroup will specify the Measurement Mode for childView. The following describes three measurement modes:
EXACTLY: Specifies the exact value. Generally, when childView sets its width and height to the exact value or match_parent, ViewGroup sets it to EXACTLY;
AT_MOST: indicates that the sub-layout is limited to a maximum value. Generally, when the width and height of childView are set to wrap_content, ViewGroup sets it to AT_MOST;
UNSPECIFIED: indicates the size of the sub-layout. It usually appears in the heightMode of the item in AadapterView and in the childView of ScrollView. This mode is rare.
Note: Each row above has a general meaning that the above is not absolute, and the mode setting of childView will also have a certain relationship with the measurement mode of ViewGroup; of course, this is the first custom ViewGroup, and most of the cases are based on the above rules. For the sake of ease of understanding, I will not discuss other content in depth.
3. analysis from the API perspective
The responsibilities of ViewGroup and View are described above. The following is an analysis from the API perspective.
View determines its width and height (completed in onMeasure) based on the measured value and mode of ViewGroup, and then draws its own in onDraw.
ViewGroup needs to pass in the measurement value and mode of the View to the view (completed in onMeasure), and for the parent layout of the ViewGroup, you also need to determine the width and height of the View in onMeasure. In addition, you need to specify the location of its childView in onLayout.
4. Complete example
Requirement: we define a ViewGroup. We can input 0 to 4 childView internally, which are displayed in the upper left corner, upper right corner, lower left corner, and lower right corner respectively.
1. Determine the LayoutParams of the ViewGroup.
For this example, we only need ViewGroup to support margin, so we can directly use the system's MarginLayoutParams
@Overridepublic ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs){return new MarginLayoutParams(getContext(), attrs);}
Override this method of the parent class and return the MarginLayoutParams instance. In this way, the LayoutParams of our ViewGroup is specified as MarginLayoutParams.
2. onMeasure
In onMeasure, calculate the childView's measurement value and mode, and set your own width and height:
/*** Calculate the width and height of all ChildView and set the width and height of the ChildView based on the calculation result of ChildView. */@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {/*** obtain the width, height, and calculation mode recommended by the ViewGroup container at the upper level */int widthMode = MeasureSpec. getMode (widthMeasureSpec); int heightMode = MeasureSpec. getMode (heightMeasureSpec); int sizeWidth = MeasureSpec. getSize (widthMeasureSpec); int sizeHeight = MeasureSpec. getSize (heightMeasureSpec); // calculate The width and height of all childView measureChildren (widthMeasureSpec, heightMeasureSpec);/*** if the record is wrap_content, It is Set width and height */int width = 0; int height = 0; int cCount = getChildCount (); int cWidth = 0; int cHeight = 0; MarginLayoutParams cParams = null; // calculate the height of the two childView on the left. int lHeight = 0; // calculate the height of the two childView s on the right. The final height is taken from the distance int rHeight = 0; // calculate the width of int tWidth = 0 for the two childView s on the top; // calculate the width of the following two childiew values. The final width is obtained using int bWidth = 0;/*** calculate the width and height of the container Based on childView and the configured margin. It is mainly used when the container is warp_content */for (int I = 0; I <cCount; I ++) {View childView = getChildAt (I); cWidth = childView. getMeasuredWidth (); cHeight = childView. getMeasuredHeight (); cParams = (MarginLayoutParams) childView. getLayoutParams (); // The two childViewif (I = 0 | I = 1) {tWidth + = cWidth + cParams. leftMargin + cParams. rightMargin;} if (I = 2 | I = 3) {bWidth + = CWidth + cParams. leftMargin + cParams. rightMargin;} if (I = 0 | I = 2) {lHeight + = cHeight + cParams. topMargin + cParams. bottomMargin;} if (I = 1 | I = 3) {rHeight + = cHeight + cParams. topMargin + cParams. bottomMargin;} width = Math. max (tWidth, bWidth); height = Math. max (lHeight, rHeight);/*** if wrap_content is set to the value we calculated * otherwise: directly set it to The value calculated by the parent container */setMeasuredDimension (widthMode = MeasureSpec. EXACTLY)? SizeWidth: width, (heightMode = MeasureSpec. EXACTLY )? SizeHeight: height );}
10-14 rows to obtain the calculation mode and size set for the parent ViewGroup container. In most cases, the parent container can correctly calculate its size if it is not wrap_content. Therefore, we need to calculate the width and height of wrap_content. How can we calculate it? It is calculated by the width and height of its childView.
Row 17: The measureChildren method of ViewGroup is used to set the width and height of all its children. After this row is executed, the width and height of childView are correctly calculated.
Lines 43-71 calculate the width and height of ViewGroup in wrap_content Based on childView's width and height and margin.
Rows 80-82. If the attribute value of width and height is wrap_content, the value is set to the value calculated in rows 43-71. Otherwise, the width and height entered by the parent container are used.
3. onLayout locates all its childView databases (sets the painting area of childView)
// Abstract method in viewgroup @ Overrideprotected void onLayout (boolean changed, int l, int t, int r, int B) {int cCount = getChildCount (); int cWidth = 0; int cHeight = 0; MarginLayoutParams cParams = null;/*** traverse all childView layout based on its width and height and margin */for (int I = 0; I <cCount; I ++) {View childView = getChildAt (I); cWidth = childView. getMeasuredWidth (); cHeight = childView. getMeasuredHeight (); cParams = (MarginLayoutParams) childView. getLayoutParams (); int cl = 0, ct = 0, cr = 0, cb = 0; switch (I) {case 0: cl = cParams. leftMargin; ct = cParams. topMargin; break; case 1: cl = getWidth ()-cWidth-cParams. leftMargin-cParams. rightMargin; ct = cParams. topMargin; break; case 2: cl = cParams. leftMargin; ct = getHeight ()-cHeight-cParams. bottomMargin; break; case 3: cl = getWidth ()-cWidth-cParams. leftMargin-cParams. rightMargin; ct = getHeight ()-cHeight-cParams. bottomMargin; break;} cr = cl + cWidth; cb = cHeight + ct; childView. layout (cl, ct, cr, cb );}}
The code is easy to understand: traverse all the childView S, set the childView s in the top left, top right, bottom left, and bottom right of the childView s based on the childView s width, height, and margin.
If it is the first View (index = 0): Then childView. layout (cl, ct, cr, cb); cl is leftMargin of childView, ct is topMargin, cr is cl + cWidth, cb is ct + cHeight
For the second View (index = 1): childView. layout (cl, ct, cr, cb );
Cl is getWidth ()-cWidth-cParams. leftMargin-cParams. rightMargin;
Ct is topMargin, cr is cl + cWidth, cb is ct + cHeight
The remaining two are similar ~
This completes the compilation of our ViewGroup code. Next we will test and set the width and height as fixed values, wrap_content and match_parent respectively.
5. Test Result layout 1:
Set the ViewGroup width and height to a fixed value.
:
Layout 2:
Set the width and height of ViewGroup to wrap_content.
:
Layout 3:
Set the width and height of ViewGroup to match_parent.
We can see that no matter how the width and height of ViewGroup are defined, our requirements have achieved the expected results ~~
Now, through this tutorial, we hope you can take a preliminary look at the steps for customizing ViewGroup. You can try to customize ViewGroup to achieve the effect of LinearLayout ~~
Finally, this is the first ViewGroup tutorial. We will further discuss how to better customize ViewGroup ~~~
If you think this article is useful to you, like one or leave a comment ~