FlowLayout (II)-Implementation of FlowLayout and implementation of flowlayout

Source: Internet
Author: User

FlowLayout (II)-Implementation of FlowLayout and implementation of flowlayout

My biggest dream is to wait for me to sit in a rocking chair and look back for my life. I have a story to tell my children .......


Related Articles:

1. FlowLayout (1) -- onMeasure () and onLayout ()

2. FlowLayout (2) -- FlowLayout implementation


After laying the groundwork for the previous article, this article officially began the development of FlowLayout. I 'd like to give you the following results:


The layout of the container at the bottom should be layout_width = "match_parent", layout_height = "wrap_content ";
Well, I don't have to talk about it anymore. Now we start to get started.

1. XML layout the layout diagram shows that FlowLayout contains a lot of textviews. The layout code is as follows:
First define a style, which is defined for TextView in FlowLayout:

<style name="text_flag_01">    <item name="android:layout_width">wrap_content</item>    <item name="android:layout_height">wrap_content</item>    <item name="android:layout_margin">4dp</item>    <item name="android:background">@drawable/flag_01</item>    <item name="android:textColor">#ffffff</item></style>
Note !!! We have defined margin here. Do you still remember how to extract the Margin value in the previous article? Override the generateLayoutParams () function.
The layout code of activity_main.xml is as follows:
<LinearLayout xmlns: android = "http://schemas.android.com/apk/res/android" xmlns: tools = "http://schemas.android.com/tools" android: layout_width = "match_parent" android: layout_height = "match_parent" tools: context = ". mainActivity "> <com. example. harvic. myapplication. flowLayout android: layout_width = "match_parent" android: layout_height = "wrap_content" android: background = "# ff00ff"> <TextView style = "@ style/text_flag_01" android: background = "@ drawable/flag_03" android: text = "Welcome" android: textColor = "#43BBE7"/> <TextView style = "@ style/text_flag_01" android: background = "@ drawable/flag_03" android: text = "IT engineer" android: textColor = "#43BBE7"/> <TextView style = "@ style/text_flag_01" android: background = "@ drawable/flag_03" android: text = "I really can" android: textColor = "#43BBE7"/> <TextView style = "@ style/text_flag_01" android: background = "@ drawable/flag_03" android: text = "what do you think" android: textColor = "#43BBE7"/> <TextView style = "@ style/text_flag_01" android: background = "@ drawable/flag_03" android: text = "don't just know how to make money" android: textColor = "#43BBE7"/> <TextView style = "@ style/text_flag_01" android: background = "@ drawable/flag_03" android: text = "ing" android: textColor = "#43BBE7"/> <TextView style = "@ style/text_flag_01" android: background = "@ drawable/flag_03" android: text = "I thick I can" android: textColor = "#43BBE7"/> </com. example. harvic. myapplication. flowLayout> </LinearLayout>
Pay attention to the following two points: android: layout_width of FlowLayout is set to "match_parent", android: layout_height is set to "" wrap_content, we add a background for FlowLayout to clearly show the size of the calculated region.
2. Extracting margin and onMeasure () Rewriting 1. Extracting margin

In the previous article, we mentioned that to extract margin, we must rewrite generateLayoutParams.

@Overrideprotected LayoutParams generateLayoutParams(LayoutParams p){    return new MarginLayoutParams(p);}@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs){    return new MarginLayoutParams(getContext(), attrs);}@Overrideprotected LayoutParams generateDefaultLayoutParams(){    return new MarginLayoutParams(LayoutParams.MATCH_PARENT,            LayoutParams.MATCH_PARENT);}
The specific reason is that we will not talk about it anymore. I have already talked about it very well. Next I will look at how to calculate the location of the current container in onMeasure.
2. Override onMeasure () -- calculate the width and height occupied by the current FlowLayout. here we need to rewrite the onMeasure () function to calculate the size occupied by all current containers.
To do FlowLayout, the following problems are involved:
(1) When to wrap
We can see that the layout of FlowLayout is one row. If the current row does not fit the next control, move the control to the next row for display. Therefore, we need a variable to calculate the width occupied by the current row to determine whether the remaining space can accommodate the next control.
(2) how to obtain the FlowLayout width
The width of FlowLayout is the maximum width of all rows. Therefore, we need to record the width of each row and find the maximum value among all values.
(3) how to get the height of FlowLayout
Apparently, the height of FlowLayout is the sum of the heights of each row, and the height of each row is the maximum value of the height of all controls in the row.
The principle is over here. Let's look at the code below:

(1) first, when we first came in, we used MeasureSpec to obtain the recommended value mode.

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    int measureWidth = MeasureSpec.getSize(widthMeasureSpec);    int measureHeight = MeasureSpec.getSize(heightMeasureSpec);    int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);    int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);    ………………}
(2) Calculate the space occupied by FlowLayout.
Apply for several variables first:
Int lineWidth = 0; // record the width of each line int lineHeight = 0; // record the height of each line int height = 0; // record the height of the entire FlowLayout int width = 0; // record the width of the entire FlowLayout
Then start computing: (paste the code first, and then elaborate)
Int count = getChildCount (); for (int I = 0; I <count; I ++) {View child = getChildAt (I); measureChild (child, widthMeasureSpec, heightMeasureSpec ); marginLayoutParams lp = (MarginLayoutParams) child. getLayoutParams (); int childWidth = child. getMeasuredWidth () + lp. leftMargin + lp. rightMargin; int childHeight = child. getMeasuredHeight () + lp. topMargin + lp. bottomMargin; if (lineWidth + childWidth> measureWidth) {// line feed width = Math. max (lineWidth, childWidth); height + = lineHeight; // adjust this control to the next row because it cannot fit the current control, therefore, initialize the height and width of this control to lineHeight, lineWidth lineHeight = childHeight; lineWidth = childWidth;} else {// otherwise, the accumulated value lineWidth and lineHeight take the maximum height lineHeight = Math. max (lineHeight, childHeight); lineWidth + = childWidth;} // the last row does not exceed the width range. Therefore, if (I = count-1) must be processed separately) {height + = lineHeight; width = Math. max (width, lineWidth );}}
When traversing each control in the For loop, calculate the width and height of each sub-control. The Code is as follows:
View child = getChildAt(i);measureChild(child,widthMeasureSpec,heightMeasureSpec);MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth() + lp.leftMargin +lp.rightMargin;int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
Note: When calculating the height and width of the control, add the upper, lower, left, and right margin values.
Note that before calling child. getMeasuredWidth () and child. getMeasuredHeight (), you must call measureChild (child, widthMeasureSpec, heightMeasureSpec );!!!! As mentioned in the previous article, getMeasuredWidth () can be called to obtain the value after onMeasure (). Similarly, getWidth () can only obtain the value after onLayout () is called.

The following code checks whether the current control has a line break and calculates the maximum height and width:

If (lineWidth + childWidth> measureWidth) {// line feed width = Math. max (lineWidth, width); height + = lineHeight; // adjust the control to the next row because it cannot fit the current control, therefore, initialize the height and width of this control to lineHeight, lineWidth lineHeight = childHeight; lineWidth = childWidth;} else {// otherwise, the accumulated value lineWidth and lineHeight take the maximum height lineHeight = Math. max (lineHeight, childHeight); lineWidth + = childWidth ;}
Because lineWidth is used to accumulate the total width of the current row, when lineWidth + childWidth> measureWidth, it indicates that the current control is no longer available, and this control needs to be transferred to the next row; let's first look at the else section, that is, what should we do without changing the line?
When the row is not changed, the maximum height of the current row is calculated, and the width of the current Child control is accumulated to lineWidth:
lineHeight = Math.max(lineHeight,childHeight);lineWidth += childWidth;
When you need to change the rows, first calculate the newest largest row width by comparing the lineWidth of the current row with the current largest row width, as the width occupied by the current FlowLayout, you also need to add the lineHeight of the row to the height variable to calculate the total height occupied by FlowLayout.
width = Math.max(lineWidth,width);height += lineHeight;
The following is the reinitialization of lineWidth and lineHeight. Due to the line feed, the current control is the first control of the next row. Therefore, the Row Height of the current row is the height of this control, the row width of the current row is the width value of this control:
lineHeight = childHeight;lineWidth = childWidth;
It is worth noting that when calculating the last row, because it will certainly not exceed the row width, we only perform the following processing in the for loop when it does not exceed the row width:
// The else part of the above if statement} else {// otherwise the accumulated value lineWidth and lineHeight take the maximum height lineHeight = Math. max (lineHeight, childHeight); lineWidth + = childWidth ;}
Here, we only calculate the width and height of the row, but do not calculate the width and height of the row. Therefore, when it is the last control of the last row, we need to calculate the width and height separately:
// The last row does not exceed the width range. Therefore, if (I = count-1) {height + = lineHeight; width = Math. max (width, lineWidth );}
(3) Finally, set it to the system through setMeasuredDimension:
setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? measureWidth        : width, (measureHeightMode == MeasureSpec.EXACTLY) ? measureHeight        : height);
The complete code is as follows:
Protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {super. onMeasure (widthMeasureSpec, heightMeasureSpec); int measureWidth = MeasureSpec. getSize (widthMeasureSpec); int measureHeight = MeasureSpec. getSize (heightMeasureSpec); int measureWidthMode = MeasureSpec. getMode (widthMeasureSpec); int measureHeightMode = MeasureSpec. getMode (heightMeasureSpec); int lineWidth = 0; int lineHeig Ht = 0; int height = 0; int width = 0; int count = getChildCount (); for (int I = 0; I <count; I ++) {View child = getChildAt (I); measureChild (child, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams lp = (MarginLayoutParams) child. getLayoutParams (); int childWidth = child. getMeasuredWidth () + lp. leftMargin + lp. rightMargin; int childHeight = child. getMeasuredHeight () + lp. topMargin + lp. bottomMargin; If (lineWidth + childWidth> measureWidth) {// line feed width = Math. max (lineWidth, width); height + = lineHeight; // adjust the control to the next row because it cannot fit the current control, therefore, initialize the height and width of this control to lineHeight, lineWidth lineHeight = childHeight; lineWidth = childWidth;} else {// otherwise, the accumulated value lineWidth and lineHeight take the maximum height lineHeight = Math. max (lineHeight, childHeight); lineWidth + = childWidth;} // the last row does not exceed the width range. Therefore, if (I = count-1) must be processed separately) {height + = lineH Eight; width = Math. max (width, lineWidth) ;}// when the attribute is MeasureSpec. when EXACTLY is used, its height is determined. // its size is determined based on the size of the internal control only when it is wrap_content. Its size is uncertain and its attribute is AT_MOST, in this case, we need to calculate the expected size and set it to setMeasuredDimension (measureWidthMode = MeasureSpec. EXACTLY )? MeasureWidth: width, (measureHeightMode = MeasureSpec. EXACTLY )? MeasureHeight: height );}
3. Override onLayout ()-layout all child controls in onLayout () are layout child controls one by one. Because the controls need to be moved back and wrap, therefore, we need to mark the left and top coordinates of the current control. Therefore, we need to apply the following variables first:

Protected void onLayout (boolean changed, int l, int t, int r, int B) {int count = getChildCount (); int lineWidth = 0; // accumulate the row width of the current row int lineHeight = 0; // The Row Height of the current row int top = 0, left = 0; // top coordinate and left coordinate of the current coordinate ..................}
Then, calculate the top coordinates and left coordinates of each control, and call layout (int left, int top, int right, int bottom) to layout each sub-control, the Code is as follows: (list all the code first, and then elaborate)
For (int I = 0; I <count; I ++) {View child = getChildAt (I); MarginLayoutParams lp = (MarginLayoutParams) child. getLayoutParams (); int childWidth = child. getMeasuredWidth () + lp. leftMargin + lp. rightMargin; int childHeight = child. getMeasuredHeight () + lp. topMargin + lp. bottomMargin; if (childWidth + lineWidth> getMeasuredWidth () {// if the line feed top + = lineHeight; left = 0; lineHeight = childHeight; lineWidth = childWidth ;} else {lineHeight = Math. max (lineHeight, childHeight); lineWidth + = childWidth;} // calculate left, top, right, bottom int lc = left + lp of childView. leftMargin; int tc = top + lp. topMargin; int rc = lc + child. getMeasuredWidth (); int bc = tc + child. getMeasuredHeight (); child. layout (lc, tc, rc, bc); // set left as the starting point of the next child control left + = childWidth ;}
(1) first, like onMeasure (), calculate the current Child's width and height:
View child = getChildAt(i);MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;int childHeight = child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;
(2) Calculate the top coordinate and left coordinate of the row Control Based on whether to wrap the line:
If (childWidth + lineWidth> getMeasuredWidth () {// if the line breaks, the current control will run to the next row, starting from the leftmost, so left is 0, while top needs to add the Row Height of the previous row, which is the top point of the control; top + = lineHeight; left = 0; // Similarly, reinitialize lineHeight and lineWidth lineHeight = childHeight; lineWidth = childWidth;} else {// otherwise, the accumulated value lineWidth and lineHeight take the maximum height lineHeight = Math. max (lineHeight, childHeight); lineWidth + = childWidth ;}
After left and top are calculated, the top, bottom, left, and right coordinates of the control are calculated respectively:
Note that margin is not padding, and the distance between margin is not drawn inside the control, but the interval between controls!
Int lc = left + lp. leftMargin; // The left coordinate + the left margin is the start position of the control. int tc = top + lp. topMargin; // Similarly, the top coordinate plus the top margin int rc = lc + child. getMeasuredWidth (); int bc = tc + child. getMeasuredHeight (); child. layout (lc, tc, rc, bc );
Finally, calculate the location of the next coordinate: Since the top coordinate is changed only when the line is changed, you only need to change the left coordinate when the draw of a control ends:
// Set left to the start point left + = childWidth of the next child control;
The complete onLayout code is as follows:
Protected void onLayout (boolean changed, int l, int t, int r, int B) {int count = getChildCount (); int lineWidth = 0; int lineHeight = 0; int top = 0, left = 0; for (int I = 0; I <count; I ++) {View child = getChildAt (I); MarginLayoutParams lp = (MarginLayoutParams) child. getLayoutParams (); int childWidth = child. getMeasuredWidth () + lp. leftMargin + lp. rightMargin; int childHeight = child. getMeasuredHeight () + lp. topMargin + lp. bottomMargin; if (childWidth + lineWidth> getMeasuredWidth () {// if a line break occurs, the current control runs to the next line, starting from the leftmost, so left is 0, while top needs to add the Row Height of the previous row, which is the top point of the control; top + = lineHeight; left = 0; // Similarly, reinitialize lineHeight and lineWidth lineHeight = childHeight; lineWidth = childWidth;} else {lineHeight = Math. max (lineHeight, childHeight); lineWidth + = childWidth;} // calculate left, top, right, bottom int lc = left + lp of childView. leftMargin; int tc = top + lp. topMargin; int rc = lc + child. getMeasuredWidth (); int bc = tc + child. getMeasuredHeight (); child. layout (lc, tc, rc, bc); // set left as the starting point of the next child control left + = childWidth ;}}
Well, this is the end of the FlowLayout series. It mainly involves the knowledge about the Drawing Process of ViewGroup. I hope you can master it. This article is a bit messy and difficult. Anything related to code is always difficult to control. It may still be difficult to control the language. For more information, see the source code. After understanding the previous article, this article is not difficult.

The source code is provided at the bottom of the article. References:

1. Android learning notes] View's measure process Learning

2. Analysis of the measure process, WRAP_CONTENT explanation, and xml layout file parsing process in Android (II)

3. Comprehensive Analysis of ViewGroup in [Android]

4. Android cold knowledge: LayoutParams

5. MeasureSpec introduction and Usage Details

6. Android custom ViewGroup practice-> implement FlowLayout

7. Analysis of View rendering process and invalidate () in Android

8. An in-depth understanding of the layout of onMeasure in android

9. What is the relationship between the measurement modes in MeasureSpec and match_parent and wrap_content?

10. Complete parsing of the Android View rendering process, giving you a step-by-step insight into View (2)

11. Learning onMeasure () and onLayout () methods by rewriting ViewGroup

12. Android development practices: onLayout () Analysis of custom ViewGroup



If this article helps you, please pay attention to it.

Source code: http://download.csdn.net/detail/harvic880925/8928371

Please respect the copyright of the original creator, reprinted please indicate the source: http://blog.csdn.net/harvic880925/article/details/47035455, thank you


Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.

Related 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.