Created
July 30, 2014 13:50
-
-
Save tbruyelle/c3a24baa410d1cb0649f to your computer and use it in GitHub Desktop.
FlowLayout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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