Skip to content

Instantly share code, notes, and snippets.

@tbruyelle
Created July 30, 2014 13:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tbruyelle/c3a24baa410d1cb0649f to your computer and use it in GitHub Desktop.
Save tbruyelle/c3a24baa410d1cb0649f to your computer and use it in GitHub Desktop.
FlowLayout
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FlowLayout">
<attr name="verticalSpacing" format="dimension"/>
<attr name="horizontalSpacing" format="dimension"/>
<attr name="fillSpace" format="boolean"/>
</declare-styleable>
</resources>
package com.comalia.gesicamobile.manager.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.comalia.gesicamobile.manager.R;
import java.util.ArrayList;
public class FlowLayout extends ViewGroup {
private int mHorizontalSpacing;
private int mVerticalSpacing;
private boolean mFillSpace;
public FlowLayout(Context context) {
super(context);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
readAttrs(context, attrs);
}
private void readAttrs(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
try {
mHorizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
mVerticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
mFillSpace = a.getBoolean(R.styleable.FlowLayout_fillSpace, true);
} finally {
a.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = 0;
int height = getPaddingTop();
int currentWidth = getPaddingLeft();
int currentHeight = 0;
int count = getChildCount();
// store how the child will be arranged in lines
ArrayList<ArrayList<View>> lines = new ArrayList<ArrayList<View>>();
ArrayList<View> line = new ArrayList<View>();
lines.add(line);
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
measureChild(child, widthMeasureSpec, heightMeasureSpec);
boolean newline = currentWidth + child.getMeasuredWidth() + getPaddingRight() > widthSize;
if (newline) {
height += currentHeight + mVerticalSpacing;
currentHeight = 0;
width = Math.max(width, currentWidth - mHorizontalSpacing);
currentWidth = getPaddingLeft();
line = new ArrayList<View>();
lines.add(line);
}
line.add(child);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.x = currentWidth;
lp.y = height;
currentWidth += child.getMeasuredWidth() + mHorizontalSpacing;
currentHeight = Math.max(currentHeight, child.getMeasuredHeight());
}
height += currentHeight;
width = Math.max(width, currentWidth - mHorizontalSpacing);
width += getPaddingRight();
height += getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
if (mFillSpace) {
// loop on lines and rearrange child measures to fit available space
int widthPadded = widthSize - getPaddingLeft() - getPaddingRight();
for (ArrayList<View> l : lines) {
// get the max height and available width
int maxHeight = 0, lineWidth = 0;
for (View child : l) {
if (child.getMeasuredHeight() > maxHeight) {
maxHeight = child.getMeasuredHeight();
}
lineWidth += child.getMeasuredWidth();
}
int availableWidth = (widthPadded - lineWidth - (mHorizontalSpacing * (l.size() - 1))) / l.size();
// redefine child size
int xShift = 0;
for (View child : l) {
int w = child.getMeasuredWidth() + availableWidth;
int wSpec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
int hSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
((LayoutParams) child.getLayoutParams()).x += xShift;
child.measure(wSpec, hSpec);
xShift += availableWidth;
}
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0, count = getChildCount(); i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
}
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
}
public static class LayoutParams extends ViewGroup.LayoutParams {
private int x, y;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment