Java Android的断线小部件布局

Java Android的断线小部件布局,java,android,user-interface,layout,Java,Android,User Interface,Layout,我正在尝试创建一个向用户显示一些数据的活动。数据可以分为“单词”,每个单词都是一个小部件,“单词”的序列将形成数据(“句子”),即包含单词的ViewGroup小部件。由于一个“句子”中所有“单词”所需的空间将超过显示屏上可用的水平空间,因此我希望将这些“句子”包装为普通文本 以下代码: public class WrapTest extends Activity { /** Called when the activity is first created. */ @Overri

我正在尝试创建一个向用户显示一些数据的活动。数据可以分为“单词”,每个单词都是一个小部件,“单词”的序列将形成数据(“句子”),即包含单词的ViewGroup小部件。由于一个“句子”中所有“单词”所需的空间将超过显示屏上可用的水平空间,因此我希望将这些“句子”包装为普通文本

以下代码:

public class WrapTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout l = new LinearLayout(this);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
        LinearLayout.LayoutParams mlp = new LinearLayout.LayoutParams(
                new ViewGroup.MarginLayoutParams(
                        LinearLayout.LayoutParams.WRAP_CONTENT,
                        LinearLayout.LayoutParams.WRAP_CONTENT));
        mlp.setMargins(0, 0, 2, 0);

        for (int i = 0; i < 10; i++) {
            TextView t = new TextView(this);
            t.setText("Hello");
            t.setBackgroundColor(Color.RED);
            t.setSingleLine(true);
            l.addView(t, mlp);
        }

        setContentView(l, lp);
    }
}
公共类WrapTest扩展活动{
/**在首次创建活动时调用*/
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
线性布局l=新的线性布局(本);
LinearLayout.LayoutParams lp=新的LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL\u父级,
LinearLayout.LayoutParams.WRAP_内容);
LinearLayout.LayoutParams mlp=新的LinearLayout.LayoutParams(
新建ViewGroup.MarginLayoutParams(
LinearLayout.LayoutParams.WRAP_内容,
LinearLayout.LayoutParams.WRAP_内容);
mlp.setMargins(0,0,2,0);
对于(int i=0;i<10;i++){
TextView t=新的TextView(此);
t、 setText(“你好”);
t、 setBackgroundColor(颜色:红色);
t、 设置单线(真);
l、 addView(t,mlp);
}
setContentView(l,lp);
}
}
生成类似于左图的内容,但我希望布局呈现与右图相同的小部件


是否存在这样的布局或布局与参数的组合,或者我必须为此实现自己的视图组?

尝试将lp的两个布局参数都设置为
WRAP\u CONTENT


mlp设置为
WRAP\u CONTENT
WRAP\u CONTENT
可确保您的文本视图t足够宽和高,足以容纳“Hello”或您放入其中的任何字符串。我认为l可能不知道你的t有多宽。
setSingleLine(true)
可能也有帮助。

我自己做了布局,可以满足我的需要,但目前它非常有限。当然欢迎提出意见和改进建议

活动:

package se.fnord.xmms2.predicate;

import se.fnord.android.layout.PredicateLayout;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.TextView;

public class Predicate extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        PredicateLayout l = new PredicateLayout(this);
        for (int i = 0; i < 10; i++) {
            TextView t = new TextView(this);
            t.setText("Hello");
            t.setBackgroundColor(Color.RED);
            t.setSingleLine(true);
            l.addView(t, new PredicateLayout.LayoutParams(2, 0));
        }

        setContentView(l);
    }
}
包se.fnord.xmms2.predicate;
导入se.fnord.android.layout.PredicateLayout;
导入android.app.Activity;
导入android.graphics.Color;
导入android.os.Bundle;
导入android.widget.TextView;
公共类谓词扩展活动{
/**在首次创建活动时调用*/
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
PredicateLayout l=新的PredicateLayout(此);
对于(int i=0;i<10;i++){
TextView t=新的TextView(此);
t、 setText(“你好”);
t、 setBackgroundColor(颜色:红色);
t、 设置单线(真);
l、 addView(t,新谓词Layout.LayoutParams(2,0));
}
setContentView(l);
}
}
或者在XML布局中:

<se.fnord.android.layout.PredicateLayout
    android:id="@+id/predicate_layout"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
/>

布局如下:

package se.fnord.android.layout;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
 * ViewGroup that arranges child views in a similar way to text, with them laid
 * out one line at a time and "wrapping" to the next line as needed.
 * 
 * Code licensed under CC-by-SA
 *  
 * @author Henrik Gustafsson
 * @see http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android
 * @license http://creativecommons.org/licenses/by-sa/2.5/
 *
 */
public class PredicateLayout extends ViewGroup {

    private int line_height;

    public PredicateLayout(Context context) {
        super(context);
    }
    
    public PredicateLayout(Context context, AttributeSet attrs){
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        assert(MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED);

        final int width = MeasureSpec.getSize(widthMeasureSpec);

        // The next line is WRONG!!! Doesn't take into account requested MeasureSpec mode!
        int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
        final int count = getChildCount();
        int line_height = 0;
        
        int xpos = getPaddingLeft();
        int ypos = getPaddingTop();
        
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                child.measure(
                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED));
                
                final int childw = child.getMeasuredWidth();
                line_height = Math.max(line_height, child.getMeasuredHeight() + lp.height);
                
                if (xpos + childw > width) {
                    xpos = getPaddingLeft();
                    ypos += line_height;
                }
                
                xpos += childw + lp.width;
            }
        }
        this.line_height = line_height;
        
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED){
            height = ypos + line_height;

        } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST){
            if (ypos + line_height < height){
                height = ypos + line_height;
            }
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(1, 1); // default of 1px spacing
    }

    @Override
    protected boolean checkLayoutParams(LayoutParams p) {
        return (p instanceof LayoutParams);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        final int width = r - l;
        int xpos = getPaddingLeft();
        int ypos = getPaddingTop();

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final int childw = child.getMeasuredWidth();
                final int childh = child.getMeasuredHeight();
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (xpos + childw > width) {
                    xpos = getPaddingLeft();
                    ypos += line_height;
                }
                child.layout(xpos, ypos, xpos + childw, ypos + childh);
                xpos += childw + lp.width;
            }
        }
    }
}
包se.fnord.android.layout;
导入android.content.Context;
导入android.util.AttributeSet;
导入android.view.view;
导入android.view.ViewGroup;
/**
*视图组,以与文本类似的方式排列子视图,并放置它们
*一次输出一行,并根据需要“包装”到下一行。
* 
*SA根据CC许可的代码
*  
*@作者Henrik Gustafsson
*@见http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android
*@许可证http://creativecommons.org/licenses/by-sa/2.5/
*
*/
公共类PredicateLayout扩展了ViewGroup{
私人内线高度;
公共谓词Layout(上下文){
超级(上下文);
}
公共谓词集合(上下文、属性集合属性){
超级(上下文,attrs);
}
@凌驾
测量时的保护空隙(内部宽度测量等级、内部高度测量等级){
断言(MeasureSpec.getMode(widthmasurespec)!=MeasureSpec.UNSPECIFIED);
最终int width=MeasureSpec.getSize(widthmasurespec);
//下一行错误!!!未考虑所请求的MeasureSpec模式!
int height=MeasureSpec.getSize(heightMeasureSpec)-getPaddingTop()-getPaddingBottom();
最终整数计数=getChildCount();
int line_高度=0;
int xpos=getPaddingLeft();
int ypos=getPaddingTop();
for(int i=0;i宽度){
xpos=getPaddingLeft();
ypos+=线路高度;
}
xpos+=childw+lp.width;
}
}
this.line_height=line_height;
if(测量等级getMode(高度测量等级)=未指定的测量等级){
高度=ypos+线路高度;
}else if(MeasureSpec.getMode(heightMeasureSpec)=MeasureSpec.AT_){
if(ypos+线路高度<高度){
高度=ypos+线路高度;
}
}
设置测量尺寸(宽度、高度);
}
@凌驾
受保护的布局参数generateDefaultLayoutPar
child.measure(
    MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
    MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
int childHeightMeasureSpec;
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
    childHeightMeasureSpec =
        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
}
else {
    childHeightMeasureSpec = 
        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);            
}
child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), 
    childHeightMeasureSpec);
package com.asolutions.widget;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import com.asolutions.widget.R;

public class RowLayout extends ViewGroup {
    public static final int DEFAULT_HORIZONTAL_SPACING = 5;
    public static final int DEFAULT_VERTICAL_SPACING = 5;
    private final int horizontalSpacing;
    private final int verticalSpacing;
    private List<RowMeasurement> currentRows = Collections.emptyList();

    public RowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.RowLayout);
        horizontalSpacing = styledAttributes.getDimensionPixelSize(R.styleable.RowLayout_android_horizontalSpacing,
                DEFAULT_HORIZONTAL_SPACING);
        verticalSpacing = styledAttributes.getDimensionPixelSize(R.styleable.RowLayout_android_verticalSpacing,
                DEFAULT_VERTICAL_SPACING);
        styledAttributes.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int maxInternalWidth = MeasureSpec.getSize(widthMeasureSpec) - getHorizontalPadding();
        final int maxInternalHeight = MeasureSpec.getSize(heightMeasureSpec) - getVerticalPadding();
        List<RowMeasurement> rows = new ArrayList<RowMeasurement>();
        RowMeasurement currentRow = new RowMeasurement(maxInternalWidth, widthMode);
        rows.add(currentRow);
        for (View child : getLayoutChildren()) {
            LayoutParams childLayoutParams = child.getLayoutParams();
            int childWidthSpec = createChildMeasureSpec(childLayoutParams.width, maxInternalWidth, widthMode);
            int childHeightSpec = createChildMeasureSpec(childLayoutParams.height, maxInternalHeight, heightMode);
            child.measure(childWidthSpec, childHeightSpec);
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            if (currentRow.wouldExceedMax(childWidth)) {
                currentRow = new RowMeasurement(maxInternalWidth, widthMode);
                rows.add(currentRow);
            }
            currentRow.addChildDimensions(childWidth, childHeight);
        }

        int longestRowWidth = 0;
        int totalRowHeight = 0;
        for (int index = 0; index < rows.size(); index++) {
            RowMeasurement row = rows.get(index);
            totalRowHeight += row.getHeight();
            if (index < rows.size() - 1) {
                totalRowHeight += verticalSpacing;
            }
            longestRowWidth = Math.max(longestRowWidth, row.getWidth());
        }
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? MeasureSpec.getSize(widthMeasureSpec) : longestRowWidth
                + getHorizontalPadding(), heightMode == MeasureSpec.EXACTLY ? MeasureSpec.getSize(heightMeasureSpec)
                : totalRowHeight + getVerticalPadding());
        currentRows = Collections.unmodifiableList(rows);
    }

    private int createChildMeasureSpec(int childLayoutParam, int max, int parentMode) {
        int spec;
        if (childLayoutParam == LayoutParams.FILL_PARENT) {
            spec = MeasureSpec.makeMeasureSpec(max, MeasureSpec.EXACTLY);
        } else if (childLayoutParam == LayoutParams.WRAP_CONTENT) {
            spec = MeasureSpec.makeMeasureSpec(max, parentMode == MeasureSpec.UNSPECIFIED ? MeasureSpec.UNSPECIFIED
                    : MeasureSpec.AT_MOST);
        } else {
            spec = MeasureSpec.makeMeasureSpec(childLayoutParam, MeasureSpec.EXACTLY);
        }
        return spec;
    }

    @Override
    protected void onLayout(boolean changed, int leftPosition, int topPosition, int rightPosition, int bottomPosition) {
        final int widthOffset = getMeasuredWidth() - getPaddingRight();
        int x = getPaddingLeft();
        int y = getPaddingTop();

        Iterator<RowMeasurement> rowIterator = currentRows.iterator();
        RowMeasurement currentRow = rowIterator.next();
        for (View child : getLayoutChildren()) {
            final int childWidth = child.getMeasuredWidth();
            final int childHeight = child.getMeasuredHeight();
            if (x + childWidth > widthOffset) {
                x = getPaddingLeft();
                y += currentRow.height + verticalSpacing;
                if (rowIterator.hasNext()) {
                    currentRow = rowIterator.next();
                }
            }
            child.layout(x, y, x + childWidth, y + childHeight);
            x += childWidth + horizontalSpacing;
        }
    }

    private List<View> getLayoutChildren() {
        List<View> children = new ArrayList<View>();
        for (int index = 0; index < getChildCount(); index++) {
            View child = getChildAt(index);
            if (child.getVisibility() != View.GONE) {
                children.add(child);
            }
        }
        return children;
    }

    protected int getVerticalPadding() {
        return getPaddingTop() + getPaddingBottom();
    }

    protected int getHorizontalPadding() {
        return getPaddingLeft() + getPaddingRight();
    }

    private final class RowMeasurement {
        private final int maxWidth;
        private final int widthMode;
        private int width;
        private int height;

        public RowMeasurement(int maxWidth, int widthMode) {
            this.maxWidth = maxWidth;
            this.widthMode = widthMode;
        }

        public int getHeight() {
            return height;
        }

        public int getWidth() {
            return width;
        }

        public boolean wouldExceedMax(int childWidth) {
            return widthMode == MeasureSpec.UNSPECIFIED ? false : getNewWidth(childWidth) > maxWidth;
        }

        public void addChildDimensions(int childWidth, int childHeight) {
            width = getNewWidth(childWidth);
            height = Math.max(height, childHeight);
        }

        private int getNewWidth(int childWidth) {
            return width == 0 ? childWidth : width + horizontalSpacing + childWidth;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RowLayout">
        <attr name="android:verticalSpacing" />
        <attr name="android:horizontalSpacing" />
    </declare-styleable>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<com.asolutions.widget.RowLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:padding="10dp"
    android:horizontalSpacing="10dp"
    android:verticalSpacing="20dp">
    <FrameLayout android:layout_width="30px" android:layout_height="40px" android:background="#F00"/>
    <FrameLayout android:layout_width="60px" android:layout_height="40px" android:background="#F00"/>
    <FrameLayout android:layout_width="70px" android:layout_height="20px" android:background="#F00"/>
    <FrameLayout android:layout_width="20px" android:layout_height="60px" android:background="#F00"/>
    <FrameLayout android:layout_width="10px" android:layout_height="40px" android:background="#F00"/>
    <FrameLayout android:layout_width="40px" android:layout_height="40px" android:background="#F00"/>
    <FrameLayout android:layout_width="40px" android:layout_height="40px" android:background="#F00"/>
    <FrameLayout android:layout_width="40px" android:layout_height="40px" android:background="#F00"/>
</com.asolutions.widget.RowLayout>
    package com.superliminal.android.util;

    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.ViewGroup;


    /**
     * A view container with layout behavior like that of the Swing FlowLayout.
     * Originally from http://nishantvnair.wordpress.com/2010/09/28/flowlayout-in-android/ itself derived from
     * http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android
     *
     * @author Melinda Green
     */
    public class FlowLayout extends ViewGroup {
        private final static int PAD_H = 2, PAD_V = 2; // Space between child views.
        private int mHeight;

        public FlowLayout(Context context) {
            super(context);
        }

        public FlowLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED);
            final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
            int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
            final int count = getChildCount();
            int xpos = getPaddingLeft();
            int ypos = getPaddingTop();
            int childHeightMeasureSpec;
            if(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST)
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
            else
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            mHeight = 0;
            for(int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if(child.getVisibility() != GONE) {
                    child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec);
                    final int childw = child.getMeasuredWidth();
                    mHeight = Math.max(mHeight, child.getMeasuredHeight() + PAD_V);
                    if(xpos + childw > width) {
                        xpos = getPaddingLeft();
                        ypos += mHeight;
                    }
                    xpos += childw + PAD_H;
                }
            }
            if(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
                height = ypos + mHeight;
            } else if(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
                if(ypos + mHeight < height) {
                    height = ypos + mHeight;
                }
            }
            height += 5; // Fudge to avoid clipping bottom of last row.
            setMeasuredDimension(width, height);
        } // end onMeasure()

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            final int width = r - l;
            int xpos = getPaddingLeft();
            int ypos = getPaddingTop();
            for(int i = 0; i < getChildCount(); i++) {
                final View child = getChildAt(i);
                if(child.getVisibility() != GONE) {
                    final int childw = child.getMeasuredWidth();
                    final int childh = child.getMeasuredHeight();
                    if(xpos + childw > width) {
                        xpos = getPaddingLeft();
                        ypos += mHeight;
                    }
                    child.layout(xpos, ypos, xpos + childw, ypos + childh);
                    xpos += childw + PAD_H;
                }
            }
        } // end onLayout()

    }
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
 * ViewGroup that arranges child views in a similar way to text, with them laid
 * out one line at a time and "wrapping" to the next line as needed.
 * 
 * Code licensed under CC-by-SA
 *  
 * @author Henrik Gustafsson
 * @see http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android
 * @license http://creativecommons.org/licenses/by-sa/2.5/
 *
 * Updated by Aurélien Guillard
 * Each line can have a different height
 * 
 */
public class FlowLayout extends ViewGroup {

    public static class LayoutParams extends ViewGroup.LayoutParams {

        public final int horizontal_spacing;
        public final int vertical_spacing;

        /**
         * @param horizontal_spacing Pixels between items, horizontally
         * @param vertical_spacing Pixels between items, vertically
         */
        public LayoutParams(int horizontal_spacing, int vertical_spacing) {
            super(0, 0);
            this.horizontal_spacing = horizontal_spacing;
            this.vertical_spacing = vertical_spacing;
        }
    }

    public FlowLayout(Context context) {
        super(context);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED);

        final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();

        int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
        final int count = getChildCount();
        int line_height = 0;

        int xpos = getPaddingLeft();
        int ypos = getPaddingTop();

        int childHeightMeasureSpec;

        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);

        } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        } else {
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec);
                final int childw = child.getMeasuredWidth();

                if (xpos + childw > width) {
                    xpos = getPaddingLeft();
                    ypos += line_height;
                }

                xpos += childw + lp.horizontal_spacing;
                line_height = child.getMeasuredHeight() + lp.vertical_spacing;
            }
        }

        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
            height = ypos + line_height;

        } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
            if (ypos + line_height < height) {
                height = ypos + line_height;
            }
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(1, 1); // default of 1px spacing
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        if (p instanceof LayoutParams) {
            return true;
        }
        return false;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        final int width = r - l;
        int xpos = getPaddingLeft();
        int ypos = getPaddingTop();
        int lineHeight = 0;

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final int childw = child.getMeasuredWidth();
                final int childh = child.getMeasuredHeight();
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                if (xpos + childw > width) {
                    xpos = getPaddingLeft();
                    ypos += lineHeight;
                }

                lineHeight = child.getMeasuredHeight() + lp.vertical_spacing;

                child.layout(xpos, ypos, xpos + childw, ypos + childh);
                xpos += childw + lp.horizontal_spacing;
            }
        }
    }
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new MarginLayoutParams(getContext(), attrs);
}

@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    return p instanceof MarginLayoutParams;
}
    LinearLayout row = new LinearLayout(this);

    //get the size of the screen
    Display display = getWindowManager().getDefaultDisplay();
    this.screenWidth = display.getWidth();  // deprecated

    for(int i=0; i<this.users.length; i++) {

        row.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));

        this.tag_button = new Button(this);
        this.tag_button.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, 70));
        //get the size of the button text
        Paint mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(tag_button.getTextSize());
        mPaint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.NORMAL));
        float size = mPaint.measureText(tag_button.getText().toString(), 0, tag_button.getText().toString().length());
        size = size+28;
        this.totalTextWidth += size;

        if(totalTextWidth < screenWidth) {
            row.addView(tag_button);
        } else {
            this.tags.addView(row);
            row = new LinearLayout(this);
            row.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            row.addView(tag_button);
            this.totalTextWidth = size;
        }
    }
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

public class FlowLayout extends ViewGroup {

private int line_height;

public static class LayoutParams extends ViewGroup.LayoutParams {

    public final int horizontal_spacing;
    public final int vertical_spacing;

    /**
     * @param horizontal_spacing Pixels between items, horizontally
     * @param vertical_spacing Pixels between items, vertically
     */
    public LayoutParams(int horizontal_spacing, int vertical_spacing, ViewGroup.LayoutParams viewGroupLayout) {
        super(viewGroupLayout);
        this.horizontal_spacing = horizontal_spacing;
        this.vertical_spacing = vertical_spacing;
    }

    /**
     * @param horizontal_spacing Pixels between items, horizontally
     * @param vertical_spacing Pixels between items, vertically
     */
    public LayoutParams(int horizontal_spacing, int vertical_spacing) {
        super(0, 0);
        this.horizontal_spacing = horizontal_spacing;
        this.vertical_spacing = vertical_spacing;
    }
}

public FlowLayout(Context context) {
    super(context);
}

public FlowLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED);

    final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
    int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
    final int count = getChildCount();
    int line_height = 0;

    int xpos = getPaddingLeft();
    int ypos = getPaddingTop();

    int childHeightMeasureSpec;
    if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
    } else {
        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
    }


    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec);
            final int childw = child.getMeasuredWidth();
            line_height = Math.max(line_height, child.getMeasuredHeight() + lp.vertical_spacing);

            if (xpos + childw > width) {
                xpos = getPaddingLeft();
                ypos += line_height;
            }

            xpos += childw + lp.horizontal_spacing;
        }
    }
    this.line_height = line_height;

    if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
        height = ypos + line_height;

    } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
        if (ypos + line_height < height) {
            height = ypos + line_height;
        }
    }
    setMeasuredDimension(width, height);
}

@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(1, 1); // default of 1px spacing
}

@Override
protected android.view.ViewGroup.LayoutParams generateLayoutParams(
        android.view.ViewGroup.LayoutParams p) {
    return new LayoutParams(1, 1, p);
}

@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    if (p instanceof LayoutParams) {
        return true;
    }
    return false;
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    final int count = getChildCount();
    final int width = r - l;
    int xpos = getPaddingLeft();
    int ypos = getPaddingTop();

    int lastHorizontalSpacing = 0;
    int rowStartIdx = 0;

    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            final int childw = child.getMeasuredWidth();
            final int childh = child.getMeasuredHeight();
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (xpos + childw > width) {
                final int freeSpace = width - xpos + lastHorizontalSpacing;
                xpos = getPaddingLeft() + freeSpace / 2;

                for (int j = rowStartIdx; j < i; ++j) {
                    final View drawChild = getChildAt(j);
                    drawChild.layout(xpos, ypos, xpos + drawChild.getMeasuredWidth(), ypos + drawChild.getMeasuredHeight());
                    xpos += drawChild.getMeasuredWidth() + ((LayoutParams)drawChild.getLayoutParams()).horizontal_spacing;
                }

                lastHorizontalSpacing = 0;
                xpos = getPaddingLeft();
                ypos += line_height;
                rowStartIdx = i;
            } 

            child.layout(xpos, ypos, xpos + childw, ypos + childh);
            xpos += childw + lp.horizontal_spacing;
            lastHorizontalSpacing = lp.horizontal_spacing;
        }
    }

    if (rowStartIdx < count) {
        final int freeSpace = width - xpos + lastHorizontalSpacing;
        xpos = getPaddingLeft() + freeSpace / 2;

        for (int j = rowStartIdx; j < count; ++j) {
            final View drawChild = getChildAt(j);
            drawChild.layout(xpos, ypos, xpos + drawChild.getMeasuredWidth(), ypos + drawChild.getMeasuredHeight());
            xpos += drawChild.getMeasuredWidth() + ((LayoutParams)drawChild.getLayoutParams()).horizontal_spacing;
        }
    }
}
}
public class CenteredFlowLayout extends ViewGroup {

  private int lineHeight;

  private int centricHeightPadding;

  private final int halfGap;

  public static final List<View> LINE_CHILDREN = new ArrayList<View>();

  public static class LayoutParams extends ViewGroup.LayoutParams {

    public final int horizontalSpacing;

    public final int verticalSpacing;

    public LayoutParams(int horizontalSpacing, int verticalSpacing) {
      super(0, 0);
      this.horizontalSpacing = horizontalSpacing;
      this.verticalSpacing = verticalSpacing;
    }
  }

  public CenteredFlowLayout(Context context) {
    super(context);
    halfGap = getResources().getDimensionPixelSize(R.dimen.half_gap);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
    int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
    final int maxHeight = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
    final int count = getChildCount();
    int lineHeight = 0;

    int xAxis = getPaddingLeft();
    int yAxis = getPaddingTop();

    int childHeightMeasureSpec;
    if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
      childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
    } else {
      childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
    }

    for (int i = 0; i < count; i++) {
      final View child = getChildAt(i);
      if (child.getVisibility() != GONE) {
        final CentricFlowLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
        child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec);
        final int childMeasuredWidth = child.getMeasuredWidth();
        lineHeight = Math.max(lineHeight, child.getMeasuredHeight() + lp.verticalSpacing);

        if (xAxis + childMeasuredWidth > width) {
          xAxis = getPaddingLeft();
          yAxis += lineHeight;
        } else if (i + 1 == count) {
          yAxis += lineHeight;
        }

        xAxis += childMeasuredWidth + lp.horizontalSpacing;
      }
    }
    this.lineHeight = lineHeight;

    if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
      height = yAxis + lineHeight;
    } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
      if (yAxis + lineHeight < height) {
        height = yAxis + lineHeight;
      }
    }
    if (maxHeight == 0) {
      maxHeight = height + getPaddingTop();
    }
    centricHeightPadding = (maxHeight - height) / 2;
    setMeasuredDimension(width, disableCenterVertical ? height + getPaddingTop() : maxHeight);
  }

  @Override
  protected CentricFlowLayout.LayoutParams generateDefaultLayoutParams() {
    return new CentricFlowLayout.LayoutParams(halfGap, halfGap);
  }

  @Override
  protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    if (p instanceof LayoutParams) {
      return true;
    }
    return false;
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    final int count = getChildCount();
    final int width = r - l;
    int yAxis = centricHeightPadding + getPaddingTop() + getPaddingBottom();
    View child;
    int measuredWidth;
    int lineWidth = getPaddingLeft() + getPaddingRight();
    CentricFlowLayout.LayoutParams lp;
    int offset;
    LINE_CHILDREN.clear();
    for (int i = 0; i < count; i++) {
      child = getChildAt(i);
      lp = (LayoutParams) child.getLayoutParams();
      if (GONE != child.getVisibility()) {
        measuredWidth = child.getMeasuredWidth();
        if (lineWidth + measuredWidth + lp.horizontalSpacing > width) {
          offset = (width - lineWidth) / 2;
          layoutHorizontalCentricLine(LINE_CHILDREN, offset, yAxis);
          lineWidth = getPaddingLeft() + getPaddingRight() + measuredWidth + lp.horizontalSpacing;
          yAxis += lineHeight;
          LINE_CHILDREN.clear();
          LINE_CHILDREN.add(child);
        } else {
          lineWidth += measuredWidth + lp.horizontalSpacing;
          LINE_CHILDREN.add(child);
        }
      }
    }
    offset = (width - lineWidth) / 2;
    layoutHorizontalCentricLine(LINE_CHILDREN, offset, yAxis);
  }

  private void layoutHorizontalCentricLine(final List<View> children, final int offset, final int yAxis) {
    int xAxis = getPaddingLeft() + getPaddingRight() + offset;
    for (View child : children) {
      final int measuredWidth = child.getMeasuredWidth();
      final int measuredHeight = child.getMeasuredHeight();
      final CentricFlowLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
      child.layout(xAxis, yAxis, xAxis + measuredWidth, yAxis + measuredHeight);
      xAxis += measuredWidth + lp.horizontalSpacing;
    }
  }    
}
dependencies {
    compile 'com.google.android:flexbox:0.3.2'
}
  dependencies {
       implementation'com.google.android:flexbox:0.3.2'
    }