Created
September 16, 2013 15:53
-
-
Save JafarKhQ/6582532 to your computer and use it in GitHub Desktop.
a FlowLayout with ALIGNMENT_LEFT and ALIGNMENT_RIGHT
This file contains hidden or 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 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