Skip to content

Instantly share code, notes, and snippets.

@JafarKhQ
Created September 16, 2013 15:53
Show Gist options
  • Save JafarKhQ/6582532 to your computer and use it in GitHub Desktop.
Save JafarKhQ/6582532 to your computer and use it in GitHub Desktop.
a FlowLayout with ALIGNMENT_LEFT and ALIGNMENT_RIGHT
package org.apmem.tools.layouts;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* User: Romain Guy
* <p/>
* Using example: <?xml version="4.0" encoding="utf-8"?>
* <com.example.android.layout.FlowLayout
* xmlns:f="http://schemas.android.com/apk/res/org.apmem.android"
* xmlns:android="http://schemas.android.com/apk/res/android"
* f:horizontalSpacing="6dip" f:verticalSpacing="12dip"
* android:layout_width="wrap_content" android:layout_height="wrap_content"
* android:paddingLeft="6dip" android:paddingTop="6dip"
* android:paddingRight="12dip"> <Button android:layout_width="wrap_content"
* android:layout_height="wrap_content" f:layout_horizontalSpacing="32dip"
* f:layout_breakLine="true" android:text="Cancel" />
* <p/>
* </com.example.android.layout.FlowLayout>
*/
public class FlowLayout extends ViewGroup {
public static final int ORIENTATION_HORIZONTAL = 0;
public static final int ORIENTATION_VERTICAL = 1;
public static final int ALIGNMENT_LEFT = 0;
public static final int ALIGNMENT_RIGHT = 1;
private int mHorizontalSpacing = 0;
private int mVerticalSpacing = 0;
private int mOrientation = ORIENTATION_HORIZONTAL;
private int mAlignment = ALIGNMENT_LEFT;
private boolean mDebugDraw = false;
public FlowLayout(Context context) {
super(context);
this.readStyleParameters(context, null);
}
public FlowLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
this.readStyleParameters(context, attributeSet);
}
public FlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
this.readStyleParameters(context, attributeSet);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec)
- this.getPaddingRight() - this.getPaddingLeft();
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec)
- this.getPaddingRight() - this.getPaddingLeft();
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
int size;
int mode;
if (mOrientation == ORIENTATION_HORIZONTAL) {
size = sizeWidth;
mode = modeWidth;
} else {
size = sizeHeight;
mode = modeHeight;
}
int lineThicknessWithSpacing = 0;
int lineThickness = 0;
int lineLengthWithSpacing = 0;
int lineLength;
int prevLinePosition = 0;
int controlMaxLength = 0;
int controlMaxThickness = 0;
boolean forceNextToBeInNewLine = false;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(MeasureSpec.makeMeasureSpec(sizeWidth,
modeWidth == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST
: modeWidth), MeasureSpec.makeMeasureSpec(
sizeHeight,
modeHeight == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST
: modeHeight));
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int hSpacing = this.getHorizontalSpacing(lp);
int vSpacing = this.getVerticalSpacing(lp);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
int childLength;
int childThickness;
int spacingLength;
int spacingThickness;
if (mOrientation == ORIENTATION_HORIZONTAL) {
childLength = childWidth;
childThickness = childHeight;
spacingLength = hSpacing;
spacingThickness = vSpacing;
} else {
childLength = childHeight;
childThickness = childWidth;
spacingLength = vSpacing;
spacingThickness = hSpacing;
}
lineLength = lineLengthWithSpacing + childLength;
lineLengthWithSpacing = lineLength + spacingLength;
boolean newLine = lp.newLine
|| (mode != MeasureSpec.UNSPECIFIED && lineLength > size)
|| forceNextToBeInNewLine;
if (newLine) {
prevLinePosition = prevLinePosition + lineThicknessWithSpacing;
lineThickness = childThickness;
lineLength = childLength;
lineThicknessWithSpacing = childThickness + spacingThickness;
lineLengthWithSpacing = lineLength + spacingLength;
}
// TODO: need to get the widthMode for the chiledView
if (/* widthMode != MeasureSpec.EXACTLY && */lp.width == LayoutParams.MATCH_PARENT) {
forceNextToBeInNewLine = true;
} else {
forceNextToBeInNewLine = false;
}
lineThicknessWithSpacing = Math.max(lineThicknessWithSpacing,
childThickness + spacingThickness);
lineThickness = Math.max(lineThickness, childThickness);
int posX;
int posY;
if (mOrientation == ORIENTATION_HORIZONTAL) {
posX = getPaddingLeft() + lineLength - childLength;
posY = getPaddingTop() + prevLinePosition;
} else {
posX = getPaddingLeft() + prevLinePosition;
posY = getPaddingTop() + lineLength - childHeight;
}
posX = applyAligment(sizeWidth, posX, childLength);
lp.setPosition(posX, posY);
controlMaxLength = Math.max(controlMaxLength, lineLength);
controlMaxThickness = prevLinePosition + lineThickness;
}
if (mOrientation == ORIENTATION_HORIZONTAL) {
this.setMeasuredDimension(
resolveSize(controlMaxLength, widthMeasureSpec),
resolveSize(controlMaxThickness, heightMeasureSpec));
} else {
this.setMeasuredDimension(
resolveSize(controlMaxThickness, widthMeasureSpec),
resolveSize(controlMaxLength, heightMeasureSpec));
}
}
private int getVerticalSpacing(LayoutParams lp) {
int vSpacing;
if (lp.verticalSpacingSpecified()) {
vSpacing = lp.verticalSpacing;
} else {
vSpacing = this.mVerticalSpacing;
}
return vSpacing;
}
private int getHorizontalSpacing(LayoutParams lp) {
int hSpacing;
if (lp.horizontalSpacingSpecified()) {
hSpacing = lp.horizontalSpacing;
} else {
hSpacing = this.mHorizontalSpacing;
}
return hSpacing;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; 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 drawChild(Canvas canvas, View child, long drawingTime) {
boolean more = super.drawChild(canvas, child, drawingTime);
this.drawDebugInfo(canvas, child);
return more;
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
return new LayoutParams(getContext(), attributeSet);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
private void readStyleParameters(Context context, AttributeSet attributeSet) {
TypedArray a = context.obtainStyledAttributes(attributeSet,
R.styleable.FlowLayout);
try {
mHorizontalSpacing = a.getDimensionPixelSize(
R.styleable.FlowLayout_horizontalSpacing, 0);
mVerticalSpacing = a.getDimensionPixelSize(
R.styleable.FlowLayout_verticalSpacing, 0);
mOrientation = a.getInteger(R.styleable.FlowLayout_orientation,
ORIENTATION_HORIZONTAL);
mAlignment = a.getInteger(R.styleable.FlowLayout_alignment,
ALIGNMENT_LEFT);
mDebugDraw = a.getBoolean(R.styleable.FlowLayout_debugDraw, false);
} finally {
a.recycle();
}
}
private int applyAligment(int totalWidth, int posX, int viewWidth) {
int newX = posX;
if (ALIGNMENT_RIGHT == mAlignment) {
newX = (totalWidth - posX) - viewWidth + getPaddingLeft();
}
return newX;
}
private Paint createPaint(int color) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(color);
paint.setStrokeWidth(2.0f);
return paint;
}
private void drawDebugInfo(Canvas canvas, View child) {
if (!mDebugDraw) {
return;
}
Paint childPaint = this.createPaint(0xffffff00);
Paint layoutPaint = this.createPaint(0xff00ff00);
Paint newLinePaint = this.createPaint(0xffff0000);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.horizontalSpacing > 0) {
float x;
float y = child.getTop() + child.getHeight() / 2.0f;
if (ALIGNMENT_LEFT == mAlignment) {
x = child.getRight();
canvas.drawLine(x, y, x + lp.horizontalSpacing, y, childPaint);
canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y - 4.0f, x
+ lp.horizontalSpacing, y, childPaint);
canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y + 4.0f, x
+ lp.horizontalSpacing, y, childPaint);
} else {
x = child.getLeft();
canvas.drawLine(x, y, x - lp.horizontalSpacing, y, childPaint);
canvas.drawLine(x - lp.horizontalSpacing + 4.0f, y - 4.0f, x
- lp.horizontalSpacing, y, childPaint);
canvas.drawLine(x - lp.horizontalSpacing + 4.0f, y + 4.0f, x
- lp.horizontalSpacing, y, childPaint);
}
} else if (this.mHorizontalSpacing > 0) {
float x;
float y = child.getTop() + child.getHeight() / 2.0f;
if (ALIGNMENT_LEFT == mAlignment) {
x = child.getRight();
canvas.drawLine(x, y, x + this.mHorizontalSpacing, y,
layoutPaint);
canvas.drawLine(x + this.mHorizontalSpacing - 4.0f, y - 4.0f, x
+ this.mHorizontalSpacing, y, layoutPaint);
canvas.drawLine(x + this.mHorizontalSpacing - 4.0f, y + 4.0f, x
+ this.mHorizontalSpacing, y, layoutPaint);
} else {
x = child.getLeft();
canvas.drawLine(x, y, x - this.mHorizontalSpacing, y,
layoutPaint);
canvas.drawLine(x - this.mHorizontalSpacing + 4.0f, y - 4.0f, x
- this.mHorizontalSpacing, y, layoutPaint);
canvas.drawLine(x - this.mHorizontalSpacing + 4.0f, y + 4.0f, x
- this.mHorizontalSpacing, y, layoutPaint);
}
}
if (lp.verticalSpacing > 0) {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getBottom();
canvas.drawLine(x, y, x, y + lp.verticalSpacing, childPaint);
canvas.drawLine(x - 4.0f, y + lp.verticalSpacing - 4.0f, x, y
+ lp.verticalSpacing, childPaint);
canvas.drawLine(x + 4.0f, y + lp.verticalSpacing - 4.0f, x, y
+ lp.verticalSpacing, childPaint);
} else if (this.mVerticalSpacing > 0) {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getBottom();
canvas.drawLine(x, y, x, y + this.mVerticalSpacing, layoutPaint);
canvas.drawLine(x - 4.0f, y + this.mVerticalSpacing - 4.0f, x, y
+ this.mVerticalSpacing, layoutPaint);
canvas.drawLine(x + 4.0f, y + this.mVerticalSpacing - 4.0f, x, y
+ this.mVerticalSpacing, layoutPaint);
}
if (lp.newLine) {
if (mOrientation == ORIENTATION_HORIZONTAL) {
float x = child.getLeft();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y - 6.0f, x, y + 6.0f, newLinePaint);
} else {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getTop();
canvas.drawLine(x - 6.0f, y, x + 6.0f, y, newLinePaint);
}
}
}
public static class LayoutParams extends ViewGroup.LayoutParams {
private static int NO_SPACING = -1;
private int x;
private int y;
private int horizontalSpacing = NO_SPACING;
private int verticalSpacing = NO_SPACING;
private boolean newLine = false;
public LayoutParams(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
this.readStyleParameters(context, attributeSet);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams layoutParams) {
super(layoutParams);
}
public boolean horizontalSpacingSpecified() {
return horizontalSpacing != NO_SPACING;
}
public boolean verticalSpacingSpecified() {
return verticalSpacing != NO_SPACING;
}
public void setPosition(int x, int y) {
this.x = x;
this.y = y;
}
private void readStyleParameters(Context context,
AttributeSet attributeSet) {
TypedArray a = context.obtainStyledAttributes(attributeSet,
R.styleable.FlowLayout_LayoutParams);
try {
horizontalSpacing = a
.getDimensionPixelSize(
R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing,
NO_SPACING);
verticalSpacing = a
.getDimensionPixelSize(
R.styleable.FlowLayout_LayoutParams_layout_verticalSpacing,
NO_SPACING);
newLine = a.getBoolean(
R.styleable.FlowLayout_LayoutParams_layout_newLine,
false);
} finally {
a.recycle();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment