Android custom ViewGroup practice-) Implement FlowLayout

Source: Internet
Author: User

Android custom ViewGroup practice-) Implement FlowLayout

 

1. Overview

The previous article has basically introduced how to customize ViewGroup. If you do not know about ViewGroup, please refer to: Android guides you to customize ViewGroup. This article will use the method described in the previous article, here is an example: to implement FlowLayout, What Is FlowLayout? If you are familiar with Swing in Java, it will not be unfamiliar, that is, the control automatically adds the control to the right based on the width of ViewGroup, if the remaining space of the current row is insufficient, it is automatically added to the next row. It seems that all the controls are moving to the left. The first line is full and the second line is full ~ This is also called Stream layout. Android does not provide streaming layout, but in some cases, streaming layout is very suitable for use, such as keyword tags and search for the hot word list, such:

These are especially suitable for the use of FlowLayout. This blog will guide you to implement FlowLayout, and then use our own custom FlowLayout to implement the above label effect. By the way, github already has such FlowLayout, but I don't think it will affect our learning. I believe everyone understands how to use a control and write a control, it is better to teach people to fish than to teach them to fish.

2. Simple Analysis

1. For FlowLayout, You need to specify the LayoutParams. Currently, we only need to be able to identify margin, that is, use MarginLayoutParams.

2. Calculate the width and height of all childView s in onMeasure, and then calculate your own width and height based on the width and height of childView. (Of course, if it is not wrap_content, you can directly use the calculated value passed in by the parent ViewGroup)

3. layout all childView in onLayout.

3. generateLayoutParams

Because we only need to support margin, we can directly use the system's MarginLayoutParams

 

@Overridepublic ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs){return new MarginLayoutParams(getContext(), attrs);}

4. onMeasure

 

 

/*** Sets the Measurement Mode and size of the Child control according to the width and height of all child controls */@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {super. onMeasure (widthMeasureSpec, heightMeasureSpec); // obtain the Measurement Mode and size set for its parent container int sizeWidth = MeasureSpec. getSize (widthMeasureSpec); int sizeHeight = MeasureSpec. getSize (heightMeasureSpec); int modeWidth = MeasureSpec. getMode (widthMeasureSpec); int modeHeight = MeasureSpec. getMode (height MeasureSpec); Log. e (TAG, sizeWidth +, + sizeHeight); // if it is warp_content, the record width and height int width = 0; int height = 0; /*** record the width of each row. The width value is always the maximum width */int lineWidth = 0;/*** the height of each row, accumulate to height */int lineHeight = 0; int cCount = getChildCount (); // traverse each sub-element for (int I = 0; I <cCount; I ++) {View child = getChildAt (I); // measure the width and height of each child measureChild (child, widthMeasureSpec, heightMeasureSpec); // obtain the child lpMarginLayoutPa Rams lp = (MarginLayoutParams) child. getLayoutParams (); // The actual width of the current sub-space int childWidth = child. getMeasuredWidth () + lp. leftMargin + lp. rightMargin; // The actual height occupied by the current sub-space: int childHeight = child. getMeasuredHeight () + lp. topMargin + lp. bottomMargin;/*** if the current child is added, the maximum width is exceeded, and the current maximum width is given to width, class plus height and then enable the new line */if (lineWidth + childWidth> sizeWidth) {width = Math. max (lineWidth, childWidth); // obtain the largest lineWidth = childW Idth; // re-enable the new line and start recording // overlay the current height, height + = lineHeight; // enable the height of the next line of the record lineHeight = childHeight ;} else // otherwise, the accumulated value is lineWidth. The maximum lineHeight is {lineWidth + = childWidth; lineHeight = Math. max (lineHeight, childHeight);} // if it is the last one, compare the maximum width of the current record with the current lineWidth if (I = cCount-1) {width = Math. max (width, lineWidth); height + = lineHeight;} setMeasuredDimension (modeWidth = MeasureSpec. EXACTLY )? SizeWidth: width, (modeHeight = MeasureSpec. EXACTLY )? SizeHeight: height );}

First, get the Measurement Mode passed in by its parent container and the calculated value of the width and height, traverse all the childView S, and use the measureChild method to measure all the childView S. Then, based on the width and height obtained from all measurements of childView, the width and height of the ViewGroup if set to wrap_content are obtained. Finally, according to the mode, if it is MeasureSpec. EXACTLY, the width and height passed in by the parent ViewGroup are directly used; otherwise, the width and height calculated by yourself are set.

 

5. onLayout

In onLayout, the positions and sizes of all childView are specified.

 

/*** Stores all views and records by row */private List
 
  
> MAllViews = new ArrayList
  
   
> ();/*** Record the maximum height of each row */private List
   
    
MLineHeight = new ArrayList
    
     
(); @ Overrideprotected void onLayout (boolean changed, int l, int t, int r, int B) {mAllViews. clear (); mLineHeight. clear (); int width = getWidth (); int lineWidth = 0; int lineHeight = 0; // store all childViewList in each row
     
      
LineViews = new ArrayList
      
        (); Int cCount = getChildCount (); // traverse all children for (int I = 0; I <cCount; I ++) {View child = getChildAt (I ); marginLayoutParams lp = (MarginLayoutParams) child. getLayoutParams (); int childWidth = child. getMeasuredWidth (); int childHeight = child. getMeasuredHeight (); // if you need to wrap the line if (childWidth + lp. leftMargin + lp. rightMargin + lineWidth> width) {// record all views of this row and the maximum height of mLineHeight. add (lineHeight); // Save the childView of the current row, and then enable the new ArrayList to save the childViewmAllViews of the next row. add (lineViews); lineWidth = 0; // reset the line width lineViews = new ArrayList
       
         ();}/*** If line breaks are not required, add */lineWidth + = childWidth + lp. leftMargin + lp. rightMargin; lineHeight = Math. max (lineHeight, childHeight + lp. topMargin + lp. bottomMargin); lineViews. add (child);} // record the last line of mLineHeight. add (lineHeight); mAllViews. add (lineViews); int left = 0; int top = 0; // obtain the total number of rows int lineNums = mAllViews. size (); for (int I = 0; I <lineNums; I ++) {// All viewslineViews in each row = mAllViews. get (I); // The maximum lineHeight of the current ROW = mLineHeight. get (I); Log. e (TAG, row + I +: + lineViews. size () +, + lineViews); Log. e (TAG, row + I +,: + lineHeight); // traverses all Viewfor (int j = 0; j <lineViews. size (); j ++) {View child = lineViews. get (j); if (child. getVisibility () = View. GONE) {continue;} MarginLayoutParams lp = (MarginLayoutParams) child. getLayoutParams (); // calculate left, top, right, bottomint lc = left + lp of childView. leftMargin; int tc = top + lp. topMargin; int rc = lc + child. getMeasuredWidth (); int bc = tc + child. getMeasuredHeight (); Log. e (TAG, child +, l = + lc +, t = + t +, r = + rc +, B = + bc); child. layout (lc, tc, rc, bc); left + = child. getMeasuredWidth () + lp. rightMargin + lp. leftMargin;} left = 0; top + = lineHeight ;}}
       
      
     
    
   
  
 

Each Item in allViews is a List set of all views in each row.

 

MLineHeight records the maximum height of each row.

Rows 23-48 traverse all childView, used to set the value of allViews and the value of mLineHeight.

57 rows. All rows are traversed based on the length of allViews.

Line 7-91 traverses all the childView s in each row, computes, and locates left, top, right, and bottom of childView.

Row 92-93, reset left and top, and calculate the position of childView in the next row.

Okay. Now we have determined all the childView painting areas. At this point, our FlowLayout code is over ~~ It's not difficult to calm down and take a look at it ~

6. Test

I'm going to use TextView as our label, so I wrote a simple style for it:

In res/values/styles. xml:

 

 

Flag_01.xml

 

 

 
     
      
      
      
   
  
 

Layout file:

 

 

     
          
           
            
             
       
        
         
          
         
        
       
      
     
    
   
          
 

:

 

Isn't it good? Let's continue to customize the background:

Res/drawble/flog_02.xml

 

 
     
      
      
      
           
    
   
  
 

Flag_03.xml

 

 

 
     
      
      
          
   
  
 

Layout file:

 

 

     
          
           
            
             
       
        
         
          
         
        
       
      
     
    
   
          
          
           
            
             
       
        
         
          
         
        
       
      
     
    
   
      
          
           
            
             
       
        
         
          
         
        
       
      
     
    
   
  
 

:

 

Not liked yet ~~ All above are match_parent ~~ The following fixed the bottom width to implement the popular mobile development labels at the beginning of the article:

Flag_04.xml

 

 
     
      
      
      
   
  
 

Layout file:

 

 

     
      
       
        
         
       
        
        
       
      
     
    
   
  
 

:

 

Is it exactly the same ~~ O ~

 

If you think this blog is useful to you, leave a comment or comment on it ~~

 

 

 

 

 

 

 

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.