OSDN Git Service

Initial commit
[worldopac/WorldOpac.git] / WorldOpac / src / org / apmem / tools / layouts / FlowLayout.java
1 package org.apmem.tools.layouts;
2
3 /*
4
5  Copyright (c) 2011, Artem Votincev (apmem.org)
6  All rights reserved.
7
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.
18
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.
29
30  */
31
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;
40
41 public class FlowLayout extends ViewGroup {
42         public static final int HORIZONTAL = 0;
43         public static final int VERTICAL = 1;
44
45         private int horizontalSpacing = 0;
46         private int verticalSpacing = 0;
47         private int orientation = 0;
48         private boolean debugDraw = false;
49
50         public FlowLayout(Context context) {
51                 super(context);
52
53                 this.readStyleParameters(context, null);
54         }
55
56         public FlowLayout(Context context, AttributeSet attributeSet) {
57                 super(context, attributeSet);
58
59                 this.readStyleParameters(context, attributeSet);
60         }
61
62         public FlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
63                 super(context, attributeSet, defStyle);
64
65                 this.readStyleParameters(context, attributeSet);
66         }
67
68         @Override
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();
74
75                 int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
76                 int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
77
78                 int size;
79                 int mode;
80
81                 if (orientation == HORIZONTAL) {
82                         size = sizeWidth;
83                         mode = modeWidth;
84                 } else {
85                         size = sizeHeight;
86                         mode = modeHeight;
87                 }
88
89                 int lineThicknessWithSpacing = 0;
90                 int lineThickness = 0;
91                 int lineLengthWithSpacing = 0;
92                 int lineLength;
93
94                 int prevLinePosition = 0;
95
96                 int controlMaxLength = 0;
97                 int controlMaxThickness = 0;
98
99                 final int count = getChildCount();
100                 for (int i = 0; i < count; i++) {
101                         final View child = getChildAt(i);
102                         if (child.getVisibility() == GONE) {
103                                 continue;
104                         }
105
106                         child.measure(MeasureSpec.makeMeasureSpec(sizeWidth,
107                                         modeWidth == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST
108                                                         : modeWidth), MeasureSpec.makeMeasureSpec(
109                                         sizeHeight,
110                                         modeHeight == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST
111                                                         : modeHeight));
112
113                         LayoutParams lp = (LayoutParams) child.getLayoutParams();
114
115                         int hSpacing = this.getHorizontalSpacing(lp);
116                         int vSpacing = this.getVerticalSpacing(lp);
117
118                         int childWidth = child.getMeasuredWidth();
119                         int childHeight = child.getMeasuredHeight();
120
121                         int childLength;
122                         int childThickness;
123                         int spacingLength;
124                         int spacingThickness;
125
126                         if (orientation == HORIZONTAL) {
127                                 childLength = childWidth;
128                                 childThickness = childHeight;
129                                 spacingLength = hSpacing;
130                                 spacingThickness = vSpacing;
131                         } else {
132                                 childLength = childHeight;
133                                 childThickness = childWidth;
134                                 spacingLength = vSpacing;
135                                 spacingThickness = hSpacing;
136                         }
137
138                         lineLength = lineLengthWithSpacing + childLength;
139                         lineLengthWithSpacing = lineLength + spacingLength;
140
141                         boolean newLine = lp.newLine
142                                         || (mode != MeasureSpec.UNSPECIFIED && lineLength > size);
143                         if (newLine) {
144                                 prevLinePosition = prevLinePosition + lineThicknessWithSpacing;
145
146                                 lineThickness = childThickness;
147                                 lineLength = childLength;
148                                 lineThicknessWithSpacing = childThickness + spacingThickness;
149                                 lineLengthWithSpacing = lineLength + spacingLength;
150                         }
151
152                         lineThicknessWithSpacing = Math.max(lineThicknessWithSpacing,
153                                         childThickness + spacingThickness);
154                         lineThickness = Math.max(lineThickness, childThickness);
155
156                         int posX;
157                         int posY;
158                         if (orientation == HORIZONTAL) {
159                                 posX = getPaddingLeft() + lineLength - childLength;
160                                 posY = getPaddingTop() + prevLinePosition;
161                         } else {
162                                 posX = getPaddingLeft() + prevLinePosition;
163                                 posY = getPaddingTop() + lineLength - childHeight;
164                         }
165                         lp.setPosition(posX, posY);
166
167                         controlMaxLength = Math.max(controlMaxLength, lineLength);
168                         controlMaxThickness = prevLinePosition + lineThickness;
169                 }
170
171                 /* need to take far side padding into account */
172                 if (orientation == HORIZONTAL) {
173                         controlMaxLength += getPaddingRight();
174                         controlMaxThickness += getPaddingBottom();
175                 } else {
176                         controlMaxLength += getPaddingBottom();
177                         controlMaxThickness += getPaddingRight();
178                 }
179
180                 if (orientation == HORIZONTAL) {
181                         this.setMeasuredDimension(
182                                         resolveSize(controlMaxLength, widthMeasureSpec),
183                                         resolveSize(controlMaxThickness, heightMeasureSpec));
184                 } else {
185                         this.setMeasuredDimension(
186                                         resolveSize(controlMaxThickness, widthMeasureSpec),
187                                         resolveSize(controlMaxLength, heightMeasureSpec));
188                 }
189         }
190
191         private int getVerticalSpacing(LayoutParams lp) {
192                 int vSpacing;
193                 if (lp.verticalSpacingSpecified()) {
194                         vSpacing = lp.verticalSpacing;
195                 } else {
196                         vSpacing = this.verticalSpacing;
197                 }
198                 return vSpacing;
199         }
200
201         private int getHorizontalSpacing(LayoutParams lp) {
202                 int hSpacing;
203                 if (lp.horizontalSpacingSpecified()) {
204                         hSpacing = lp.horizontalSpacing;
205                 } else {
206                         hSpacing = this.horizontalSpacing;
207                 }
208                 return hSpacing;
209         }
210
211         @Override
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());
219                 }
220         }
221
222         @Override
223         protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
224                 boolean more = super.drawChild(canvas, child, drawingTime);
225                 this.drawDebugInfo(canvas, child);
226                 return more;
227         }
228
229         @Override
230         protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
231                 return p instanceof LayoutParams;
232         }
233
234         @Override
235         protected LayoutParams generateDefaultLayoutParams() {
236                 return new LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
237                                 android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
238         }
239
240         @Override
241         public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
242                 return new LayoutParams(getContext(), attributeSet);
243         }
244
245         @Override
246         protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
247                 return new LayoutParams(p);
248         }
249
250         private void readStyleParameters(Context context, AttributeSet attributeSet) {
251                 TypedArray a = context.obtainStyledAttributes(attributeSet,
252                                 R.styleable.FlowLayout);
253                 try {
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,
259                                         HORIZONTAL);
260                         debugDraw = a.getBoolean(R.styleable.FlowLayout_debugDraw, false);
261                 } finally {
262                         a.recycle();
263                 }
264         }
265
266         private void drawDebugInfo(Canvas canvas, View child) {
267                 if (!debugDraw) {
268                         return;
269                 }
270
271                 Paint childPaint = this.createPaint(0xffffff00);
272                 Paint layoutPaint = this.createPaint(0xff00ff00);
273                 Paint newLinePaint = this.createPaint(0xffff0000);
274
275                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
276
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);
293                 }
294
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);
311                 }
312
313                 if (lp.newLine) {
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);
318                         } else {
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);
322                         }
323                 }
324         }
325
326         private Paint createPaint(int color) {
327                 Paint paint = new Paint();
328                 paint.setAntiAlias(true);
329                 paint.setColor(color);
330                 paint.setStrokeWidth(2.0f);
331                 return paint;
332         }
333
334         public static class LayoutParams extends ViewGroup.LayoutParams {
335                 private static int NO_SPACING = -1;
336
337                 private int x;
338                 private int y;
339                 private int horizontalSpacing = NO_SPACING;
340                 private int verticalSpacing = NO_SPACING;
341                 private boolean newLine = false;
342
343                 public LayoutParams(Context context, AttributeSet attributeSet) {
344                         super(context, attributeSet);
345                         this.readStyleParameters(context, attributeSet);
346                 }
347
348                 public LayoutParams(int width, int height) {
349                         super(width, height);
350                 }
351
352                 public LayoutParams(ViewGroup.LayoutParams layoutParams) {
353                         super(layoutParams);
354                 }
355
356                 public boolean horizontalSpacingSpecified() {
357                         return horizontalSpacing != NO_SPACING;
358                 }
359
360                 public boolean verticalSpacingSpecified() {
361                         return verticalSpacing != NO_SPACING;
362                 }
363
364                 public void setPosition(int x, int y) {
365                         this.x = x;
366                         this.y = y;
367                 }
368
369                 private void readStyleParameters(Context context,
370                                 AttributeSet attributeSet) {
371                         TypedArray a = context.obtainStyledAttributes(attributeSet,
372                                         R.styleable.FlowLayout_LayoutParams);
373                         try {
374                                 horizontalSpacing = a
375                                                 .getDimensionPixelSize(
376                                                                 R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing,
377                                                                 NO_SPACING);
378                                 verticalSpacing = a
379                                                 .getDimensionPixelSize(
380                                                                 R.styleable.FlowLayout_LayoutParams_layout_verticalSpacing,
381                                                                 NO_SPACING);
382                                 newLine = a.getBoolean(
383                                                 R.styleable.FlowLayout_LayoutParams_layout_newLine,
384                                                 false);
385                         } finally {
386                                 a.recycle();
387                         }
388                 }
389         }
390 }