Android flow layout implementation

write in front

This article is original, please indicate the address in the form of a link for reprinting: http://kymjs.com/code/2015/06/06/01
The new project uses a new layout —— the function of Android label flow layout, which is just right I have been talking about the implementation of custom controls for everyone, and today I will tell you about the implementation of an android streaming layout.

In daily app use, we will see the flow layout of automatic line wrapping such as popular tags in the android app. Today, let's take a look at how to customize a flow layout similar to popular tags (download the source code below Finally given) This control is not implemented by me, the code is found from the Internet search flow layout. I just explain the implementation process and principles for you.
OpenSourceLaboratory

Look at the code first

 
public classFlowLayoutextendsViewGroup{
    private float mVerticalSpacing; //每个item纵向间距
    private float mHorizontalSpacing; //每个item横向间距

    publicFlowLayout(Contextcontext){
        super(context);
    }
    publicFlowLayout(Contextcontext,AttributeSetattrs){
        super(context, attrs);
    }
    publicvoidsetHorizontalSpacing(floatpixelSize){
        mHorizontalSpacing = pixelSize;
    }
    publicvoidsetVerticalSpacing(floatpixelSize){
        mVerticalSpacing = pixelSize;
    }
    @Override
    protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
        int selfWidth = resolveSize(0, widthMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int childLeft = paddingLeft;
        int childTop = paddingTop;
        int lineHeight = 0;

        //通过计算每一个子控件的高度,得到自己的高度
        for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
            View childView = getChildAt(i);
            LayoutParams childLayoutParams = childView.getLayoutParams();
            childView.measure(
                    getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                            childLayoutParams.width),
                    getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                            childLayoutParams.height));
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > selfWidth) {
                childLeft = paddingLeft;
                childTop += mVerticalSpacing + lineHeight;
                lineHeight = childHeight;
            } else {
                childLeft += childWidth + mHorizontalSpacing;
            }
        }

        int wantedHeight = childTop + lineHeight + paddingBottom;
        setMeasuredDimension(selfWidth, resolveSize(wantedHeight, heightMeasureSpec));
    }

    @Override
    protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
        int myWidth = r - l;

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();

        int childLeft = paddingLeft;
        int childTop = paddingTop;

        int lineHeight = 0;

        //根据子控件的宽高,计算子控件应该出现的位置。
        for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
            View childView = getChildAt(i);

            if (childView.getVisibility() == View.GONE) {
                continue;
            }

            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > myWidth) {
                childLeft = paddingLeft;
                childTop += mVerticalSpacing + lineHeight;
                lineHeight = childHeight;
            }
            childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
            childLeft += childWidth + mHorizontalSpacing;
        }
    }
}

Start with the control creation process

  1. When the flow layout is loaded into memory and displayed on the screen, the method view.measure(w,h) will be called first to measure the width and height of the view, where the parameters w and h represent the The width and height of the control's parent control.
  2. During the invocation of the view.measure() method, a callback method of the view itself, onMeasure(), is called, which is a callback method of the view itself, which is used to allow developers to recalculate their own size when customizing the View. . Generally, it will loop through this method to calculate the width and height of all descendant controls of this control.
  3. After the calculation of the width and height of the View is completed, consider displaying the control at the specified position on the screen. At this time, the onLayout() method of the view will be called. Generally, the positions of all descendant controls in this control are calculated in this method at the same time.
    Maybe the basic process is a bit boring, let's take a look at the code.

Implementation of flow layout

See this paragraph in the onMeasure() method: //Get your own height by calculating the height of each child control

 
	for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
	    View childView = getChildAt(i);
	    LayoutParams childLayoutParams = childView.getLayoutParams();
	    childView.measure(
	            getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
	                    childLayoutParams.width),
	            getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
	                    childLayoutParams.height));
	    int childWidth = childView.getMeasuredWidth();
	    int childHeight = childView.getMeasuredHeight();

	    lineHeight = Math.max(childHeight, lineHeight);

	    if (childLeft + childWidth + paddingRight > selfWidth) {
	        childLeft = paddingLeft;
	        childTop += mVerticalSpacing + lineHeight;
	        lineHeight = childHeight;
	    } else {
	        childLeft += childWidth + mHorizontalSpacing;
	    }
	}

First, through the loop, traverse all the child controls of this control, and call the measure() method of the child control at the same time. At this time, the two parameters of the measure method are the maximum width and height that the control can give the child control (as we all know, the child control then large, the displayed size cannot be larger than the parent control). The function of the getChildMeasureSpec() method here is to calculate the size (width or height) of a suitable subview, and combine the MeasureSpec information given by the LayoutParams of the subview to obtain the most suitable result. For example, if the View knows its own size (because its own MeasureSpec model is Exactly,) and the size of the subview is exactly the same as the parent window, the parent window must use the given size to layout the subview 
parameter meaning: spec The size and mode
padding passed by the parent window to the child view The margin of the parent window, that is, the android:padding
childDimension in xml The exact size that the child view wants to draw, but this value is not necessarily drawn in the end

When the size of each child control is obtained, it is simple to calculate its own width and height.
int wantedHeight = childTop + lineHeight + paddingBottom;

Similarly, this sentence in onLayout

 
	for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
	    View childView = getChildAt(i);

	    if (childView.getVisibility() == View.GONE) {
	        continue;
	    }

	    int childWidth = childView.getMeasuredWidth();
	    int childHeight = childView.getMeasuredHeight();

	    lineHeight = Math.max(childHeight, lineHeight);

	    if (childLeft + childWidth + paddingRight > myWidth) {
	        childLeft = paddingLeft;
	        childTop += mVerticalSpacing + lineHeight;
	        lineHeight = childHeight;
	    }
	    childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
	    childLeft += childWidth + mHorizontalSpacing;
	} 

First, through loop traversal, control the display position of each item sub-control, if the current line can still put the next item, put it on the current line, if not, put it on the leftmost of the next line.
In the end, the traversal is completed, which is equivalent to the completion of displaying your own position.

Effect screenshot

OpenSourceLaboratory

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326692738&siteId=291194637