I implemented something very similar to this, but using what I think is a little more standard way to handle spacing and padding. Please let me know what you guys think, and feel free to reuse without attribution:
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;publicclassRowLayoutextendsViewGroup{publicstaticfinalint DEFAULT_HORIZONTAL_SPACING =5;publicstaticfinalint DEFAULT_VERTICAL_SPACING =5;privatefinalint horizontalSpacing;privatefinalint verticalSpacing;privateList<RowMeasurement> currentRows =Collections.emptyList();publicRowLayout(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();}@Overrideprotectedvoid onMeasure(int widthMeasureSpec,int heightMeasureSpec){finalint widthMode =MeasureSpec.getMode(widthMeasureSpec);finalint heightMode =MeasureSpec.getMode(heightMeasureSpec);finalint maxInternalWidth =MeasureSpec.getSize(widthMeasureSpec)- getHorizontalPadding();finalint maxInternalHeight =MeasureSpec.getSize(heightMeasureSpec)- getVerticalPadding();List<RowMeasurement> rows =newArrayList<RowMeasurement>();RowMeasurement currentRow =newRowMeasurement(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 =newRowMeasurement(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);}privateint createChildMeasureSpec(int childLayoutParam,int max,int parentMode){int spec;if(childLayoutParam ==LayoutParams.FILL_PARENT){
spec =MeasureSpec.makeMeasureSpec(max,MeasureSpec.EXACTLY);}elseif(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;}@Overrideprotectedvoid onLayout(boolean changed,int leftPosition,int topPosition,int rightPosition,int bottomPosition){finalint widthOffset = getMeasuredWidth()- getPaddingRight();int x = getPaddingLeft();int y = getPaddingTop();Iterator<RowMeasurement> rowIterator = currentRows.iterator();RowMeasurement currentRow = rowIterator.next();for(View child : getLayoutChildren()){finalint childWidth = child.getMeasuredWidth();finalint 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;}}privateList<View> getLayoutChildren(){List<View> children =newArrayList<View>();for(int index =0; index < getChildCount(); index++){View child = getChildAt(index);if(child.getVisibility()!=View.GONE){
children.add(child);}}return children;}protectedint getVerticalPadding(){return getPaddingTop()+ getPaddingBottom();}protectedint getHorizontalPadding(){return getPaddingLeft()+ getPaddingRight();}privatefinalclassRowMeasurement{privatefinalint maxWidth;privatefinalint widthMode;privateint width;privateint height;publicRowMeasurement(int maxWidth,int widthMode){this.maxWidth = maxWidth;this.widthMode = widthMode;}publicint getHeight(){return height;}publicint getWidth(){return width;}publicboolean wouldExceedMax(int childWidth){return widthMode ==MeasureSpec.UNSPECIFIED ?false: getNewWidth(childWidth)> maxWidth;}publicvoid addChildDimensions(int childWidth,int childHeight){
width = getNewWidth(childWidth);
height =Math.max(height, childHeight);}privateint getNewWidth(int childWidth){return width ==0? childWidth : width + horizontalSpacing + childWidth;}}}
This also requires an entry under /res/values/attrs.xml, which you can create if it's not already there.
<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="RowLayout"><attr name="android:verticalSpacing"/><attr name="android:horizontalSpacing"/></declare-styleable></resources>
Usage in an xml layout looks like this:
<?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>