1 package org.apmem.tools.layouts;
5 Copyright (c) 2011, Artem Votincev (apmem.org)
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 * Neither the name of the apmem.org nor the
16 names of its contributors may be used to endorse or promote products
17 derived from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL ARTEM VOTINCEV BE LIABLE FOR ANY
23 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 import android.content.Context;
33 import android.content.res.TypedArray;
34 import android.graphics.Canvas;
35 import android.graphics.Paint;
36 import android.util.AttributeSet;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import jp.sourceforge.worldopac.R;
41 public class FlowLayout extends ViewGroup {
42 public static final int HORIZONTAL = 0;
43 public static final int VERTICAL = 1;
45 private int horizontalSpacing = 0;
46 private int verticalSpacing = 0;
47 private int orientation = 0;
48 private boolean debugDraw = false;
50 public FlowLayout(Context context) {
53 this.readStyleParameters(context, null);
56 public FlowLayout(Context context, AttributeSet attributeSet) {
57 super(context, attributeSet);
59 this.readStyleParameters(context, attributeSet);
62 public FlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
63 super(context, attributeSet, defStyle);
65 this.readStyleParameters(context, attributeSet);
69 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
70 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec)
71 - this.getPaddingRight() - this.getPaddingLeft();
72 int sizeHeight = MeasureSpec.getSize(heightMeasureSpec)
73 - this.getPaddingRight() - this.getPaddingLeft();
75 int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
76 int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
81 if (orientation == HORIZONTAL) {
89 int lineThicknessWithSpacing = 0;
90 int lineThickness = 0;
91 int lineLengthWithSpacing = 0;
94 int prevLinePosition = 0;
96 int controlMaxLength = 0;
97 int controlMaxThickness = 0;
99 final int count = getChildCount();
100 for (int i = 0; i < count; i++) {
101 final View child = getChildAt(i);
102 if (child.getVisibility() == GONE) {
106 child.measure(MeasureSpec.makeMeasureSpec(sizeWidth,
107 modeWidth == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST
108 : modeWidth), MeasureSpec.makeMeasureSpec(
110 modeHeight == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST
113 LayoutParams lp = (LayoutParams) child.getLayoutParams();
115 int hSpacing = this.getHorizontalSpacing(lp);
116 int vSpacing = this.getVerticalSpacing(lp);
118 int childWidth = child.getMeasuredWidth();
119 int childHeight = child.getMeasuredHeight();
124 int spacingThickness;
126 if (orientation == HORIZONTAL) {
127 childLength = childWidth;
128 childThickness = childHeight;
129 spacingLength = hSpacing;
130 spacingThickness = vSpacing;
132 childLength = childHeight;
133 childThickness = childWidth;
134 spacingLength = vSpacing;
135 spacingThickness = hSpacing;
138 lineLength = lineLengthWithSpacing + childLength;
139 lineLengthWithSpacing = lineLength + spacingLength;
141 boolean newLine = lp.newLine
142 || (mode != MeasureSpec.UNSPECIFIED && lineLength > size);
144 prevLinePosition = prevLinePosition + lineThicknessWithSpacing;
146 lineThickness = childThickness;
147 lineLength = childLength;
148 lineThicknessWithSpacing = childThickness + spacingThickness;
149 lineLengthWithSpacing = lineLength + spacingLength;
152 lineThicknessWithSpacing = Math.max(lineThicknessWithSpacing,
153 childThickness + spacingThickness);
154 lineThickness = Math.max(lineThickness, childThickness);
158 if (orientation == HORIZONTAL) {
159 posX = getPaddingLeft() + lineLength - childLength;
160 posY = getPaddingTop() + prevLinePosition;
162 posX = getPaddingLeft() + prevLinePosition;
163 posY = getPaddingTop() + lineLength - childHeight;
165 lp.setPosition(posX, posY);
167 controlMaxLength = Math.max(controlMaxLength, lineLength);
168 controlMaxThickness = prevLinePosition + lineThickness;
171 /* need to take far side padding into account */
172 if (orientation == HORIZONTAL) {
173 controlMaxLength += getPaddingRight();
174 controlMaxThickness += getPaddingBottom();
176 controlMaxLength += getPaddingBottom();
177 controlMaxThickness += getPaddingRight();
180 if (orientation == HORIZONTAL) {
181 this.setMeasuredDimension(
182 resolveSize(controlMaxLength, widthMeasureSpec),
183 resolveSize(controlMaxThickness, heightMeasureSpec));
185 this.setMeasuredDimension(
186 resolveSize(controlMaxThickness, widthMeasureSpec),
187 resolveSize(controlMaxLength, heightMeasureSpec));
191 private int getVerticalSpacing(LayoutParams lp) {
193 if (lp.verticalSpacingSpecified()) {
194 vSpacing = lp.verticalSpacing;
196 vSpacing = this.verticalSpacing;
201 private int getHorizontalSpacing(LayoutParams lp) {
203 if (lp.horizontalSpacingSpecified()) {
204 hSpacing = lp.horizontalSpacing;
206 hSpacing = this.horizontalSpacing;
212 protected void onLayout(boolean changed, int l, int t, int r, int b) {
213 final int count = getChildCount();
214 for (int i = 0; i < count; i++) {
215 View child = getChildAt(i);
216 LayoutParams lp = (LayoutParams) child.getLayoutParams();
217 child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y
218 + child.getMeasuredHeight());
223 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
224 boolean more = super.drawChild(canvas, child, drawingTime);
225 this.drawDebugInfo(canvas, child);
230 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
231 return p instanceof LayoutParams;
235 protected LayoutParams generateDefaultLayoutParams() {
236 return new LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
237 android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
241 public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
242 return new LayoutParams(getContext(), attributeSet);
246 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
247 return new LayoutParams(p);
250 private void readStyleParameters(Context context, AttributeSet attributeSet) {
251 TypedArray a = context.obtainStyledAttributes(attributeSet,
252 R.styleable.FlowLayout);
254 horizontalSpacing = a.getDimensionPixelSize(
255 R.styleable.FlowLayout_horizontalSpacing, 0);
256 verticalSpacing = a.getDimensionPixelSize(
257 R.styleable.FlowLayout_verticalSpacing, 0);
258 orientation = a.getInteger(R.styleable.FlowLayout_orientation,
260 debugDraw = a.getBoolean(R.styleable.FlowLayout_debugDraw, false);
266 private void drawDebugInfo(Canvas canvas, View child) {
271 Paint childPaint = this.createPaint(0xffffff00);
272 Paint layoutPaint = this.createPaint(0xff00ff00);
273 Paint newLinePaint = this.createPaint(0xffff0000);
275 LayoutParams lp = (LayoutParams) child.getLayoutParams();
277 if (lp.horizontalSpacing > 0) {
278 float x = child.getRight();
279 float y = child.getTop() + child.getHeight() / 2.0f;
280 canvas.drawLine(x, y, x + lp.horizontalSpacing, y, childPaint);
281 canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y - 4.0f, x
282 + lp.horizontalSpacing, y, childPaint);
283 canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y + 4.0f, x
284 + lp.horizontalSpacing, y, childPaint);
285 } else if (this.horizontalSpacing > 0) {
286 float x = child.getRight();
287 float y = child.getTop() + child.getHeight() / 2.0f;
288 canvas.drawLine(x, y, x + this.horizontalSpacing, y, layoutPaint);
289 canvas.drawLine(x + this.horizontalSpacing - 4.0f, y - 4.0f, x
290 + this.horizontalSpacing, y, layoutPaint);
291 canvas.drawLine(x + this.horizontalSpacing - 4.0f, y + 4.0f, x
292 + this.horizontalSpacing, y, layoutPaint);
295 if (lp.verticalSpacing > 0) {
296 float x = child.getLeft() + child.getWidth() / 2.0f;
297 float y = child.getBottom();
298 canvas.drawLine(x, y, x, y + lp.verticalSpacing, childPaint);
299 canvas.drawLine(x - 4.0f, y + lp.verticalSpacing - 4.0f, x, y
300 + lp.verticalSpacing, childPaint);
301 canvas.drawLine(x + 4.0f, y + lp.verticalSpacing - 4.0f, x, y
302 + lp.verticalSpacing, childPaint);
303 } else if (this.verticalSpacing > 0) {
304 float x = child.getLeft() + child.getWidth() / 2.0f;
305 float y = child.getBottom();
306 canvas.drawLine(x, y, x, y + this.verticalSpacing, layoutPaint);
307 canvas.drawLine(x - 4.0f, y + this.verticalSpacing - 4.0f, x, y
308 + this.verticalSpacing, layoutPaint);
309 canvas.drawLine(x + 4.0f, y + this.verticalSpacing - 4.0f, x, y
310 + this.verticalSpacing, layoutPaint);
314 if (orientation == HORIZONTAL) {
315 float x = child.getLeft();
316 float y = child.getTop() + child.getHeight() / 2.0f;
317 canvas.drawLine(x, y - 6.0f, x, y + 6.0f, newLinePaint);
319 float x = child.getLeft() + child.getWidth() / 2.0f;
320 float y = child.getTop();
321 canvas.drawLine(x - 6.0f, y, x + 6.0f, y, newLinePaint);
326 private Paint createPaint(int color) {
327 Paint paint = new Paint();
328 paint.setAntiAlias(true);
329 paint.setColor(color);
330 paint.setStrokeWidth(2.0f);
334 public static class LayoutParams extends ViewGroup.LayoutParams {
335 private static int NO_SPACING = -1;
339 private int horizontalSpacing = NO_SPACING;
340 private int verticalSpacing = NO_SPACING;
341 private boolean newLine = false;
343 public LayoutParams(Context context, AttributeSet attributeSet) {
344 super(context, attributeSet);
345 this.readStyleParameters(context, attributeSet);
348 public LayoutParams(int width, int height) {
349 super(width, height);
352 public LayoutParams(ViewGroup.LayoutParams layoutParams) {
356 public boolean horizontalSpacingSpecified() {
357 return horizontalSpacing != NO_SPACING;
360 public boolean verticalSpacingSpecified() {
361 return verticalSpacing != NO_SPACING;
364 public void setPosition(int x, int y) {
369 private void readStyleParameters(Context context,
370 AttributeSet attributeSet) {
371 TypedArray a = context.obtainStyledAttributes(attributeSet,
372 R.styleable.FlowLayout_LayoutParams);
374 horizontalSpacing = a
375 .getDimensionPixelSize(
376 R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing,
379 .getDimensionPixelSize(
380 R.styleable.FlowLayout_LayoutParams_layout_verticalSpacing,
382 newLine = a.getBoolean(
383 R.styleable.FlowLayout_LayoutParams_layout_newLine,