|
@@ -0,0 +1,326 @@
|
|
|
+package de.tudarmstadt.informatik.hostage.ui2.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;
|
|
|
+import de.tudarmstadt.informatik.hostage.R;
|
|
|
+
|
|
|
+public class FlowLayout extends ViewGroup {
|
|
|
+ public static final int HORIZONTAL = 0;
|
|
|
+ public static final int VERTICAL = 1;
|
|
|
+ private int horizontalSpacing = 0;
|
|
|
+ private int verticalSpacing = 0;
|
|
|
+ private int orientation = 0;
|
|
|
+ private boolean debugDraw = 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.getPaddingTop() - this.getPaddingBottom();
|
|
|
+
|
|
|
+ int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
|
|
|
+ int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
|
|
|
+
|
|
|
+ int size;
|
|
|
+ int mode;
|
|
|
+
|
|
|
+ if (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;
|
|
|
+
|
|
|
+ final int count = getChildCount();
|
|
|
+ for (int i = 0; i < count; i++) {
|
|
|
+ final View child = getChildAt(i);
|
|
|
+ if (child.getVisibility() == GONE) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
|
+
|
|
|
+ child.measure(
|
|
|
+ getChildMeasureSpec(widthMeasureSpec, this.getPaddingLeft() + this.getPaddingRight(), lp.width),
|
|
|
+ getChildMeasureSpec(heightMeasureSpec, this.getPaddingTop() + this.getPaddingBottom(), lp.height)
|
|
|
+ );
|
|
|
+
|
|
|
+ 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 (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);
|
|
|
+ if (newLine) {
|
|
|
+ prevLinePosition = prevLinePosition + lineThicknessWithSpacing;
|
|
|
+
|
|
|
+ lineThickness = childThickness;
|
|
|
+ lineLength = childLength;
|
|
|
+ lineThicknessWithSpacing = childThickness + spacingThickness;
|
|
|
+ lineLengthWithSpacing = lineLength + spacingLength;
|
|
|
+ }
|
|
|
+
|
|
|
+ lineThicknessWithSpacing = Math.max(lineThicknessWithSpacing, childThickness + spacingThickness);
|
|
|
+ lineThickness = Math.max(lineThickness, childThickness);
|
|
|
+
|
|
|
+ int posX;
|
|
|
+ int posY;
|
|
|
+ if (orientation == HORIZONTAL) {
|
|
|
+ posX = getPaddingLeft() + lineLength - childLength;
|
|
|
+ posY = getPaddingTop() + prevLinePosition;
|
|
|
+ } else {
|
|
|
+ posX = getPaddingLeft() + prevLinePosition;
|
|
|
+ posY = getPaddingTop() + lineLength - childHeight;
|
|
|
+ }
|
|
|
+ lp.setPosition(posX, posY);
|
|
|
+
|
|
|
+ controlMaxLength = Math.max(controlMaxLength, lineLength);
|
|
|
+ controlMaxThickness = prevLinePosition + lineThickness;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* need to take paddings into account */
|
|
|
+ if (orientation == HORIZONTAL) {
|
|
|
+ controlMaxLength += getPaddingLeft() + getPaddingRight();
|
|
|
+ controlMaxThickness += getPaddingBottom() + getPaddingTop();
|
|
|
+ } else {
|
|
|
+ controlMaxLength += getPaddingBottom() + getPaddingTop();
|
|
|
+ controlMaxThickness += getPaddingLeft() + getPaddingRight();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (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.verticalSpacing;
|
|
|
+ }
|
|
|
+ return vSpacing;
|
|
|
+ }
|
|
|
+
|
|
|
+ private int getHorizontalSpacing(LayoutParams lp) {
|
|
|
+ int hSpacing;
|
|
|
+ if (lp.horizontalSpacingSpecified()) {
|
|
|
+ hSpacing = lp.horizontalSpacing;
|
|
|
+ } else {
|
|
|
+ hSpacing = this.horizontalSpacing;
|
|
|
+ }
|
|
|
+ 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 {
|
|
|
+ horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
|
|
|
+ verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
|
|
|
+ orientation = a.getInteger(R.styleable.FlowLayout_orientation, HORIZONTAL);
|
|
|
+ debugDraw = a.getBoolean(R.styleable.FlowLayout_debugDraw, false);
|
|
|
+ } finally {
|
|
|
+ a.recycle();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void drawDebugInfo(Canvas canvas, View child) {
|
|
|
+ if (!debugDraw) {
|
|
|
+ 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 = child.getRight();
|
|
|
+ float y = child.getTop() + child.getHeight() / 2.0f;
|
|
|
+ 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.horizontalSpacing > 0) {
|
|
|
+ float x = child.getRight();
|
|
|
+ float y = child.getTop() + child.getHeight() / 2.0f;
|
|
|
+ canvas.drawLine(x, y, x + this.horizontalSpacing, y, layoutPaint);
|
|
|
+ canvas.drawLine(x + this.horizontalSpacing - 4.0f, y - 4.0f, x + this.horizontalSpacing, y, layoutPaint);
|
|
|
+ canvas.drawLine(x + this.horizontalSpacing - 4.0f, y + 4.0f, x + this.horizontalSpacing, 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.verticalSpacing > 0) {
|
|
|
+ float x = child.getLeft() + child.getWidth() / 2.0f;
|
|
|
+ float y = child.getBottom();
|
|
|
+ canvas.drawLine(x, y, x, y + this.verticalSpacing, layoutPaint);
|
|
|
+ canvas.drawLine(x - 4.0f, y + this.verticalSpacing - 4.0f, x, y + this.verticalSpacing, layoutPaint);
|
|
|
+ canvas.drawLine(x + 4.0f, y + this.verticalSpacing - 4.0f, x, y + this.verticalSpacing, layoutPaint);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (lp.newLine) {
|
|
|
+ if (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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Paint createPaint(int color) {
|
|
|
+ Paint paint = new Paint();
|
|
|
+ paint.setAntiAlias(true);
|
|
|
+ paint.setColor(color);
|
|
|
+ paint.setStrokeWidth(2.0f);
|
|
|
+ return paint;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|