FlowLayout.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /**
  2. * Copyright (c) 2011, Artem Votincev (apmem.org)
  3. * http://www.apache.org/licenses/LICENSE-2.0.txt
  4. *
  5. * Modified by Alexander Brakowski for the purpose of this project
  6. */
  7. package de.tudarmstadt.informatik.hostage.ui.layouts;
  8. import android.content.Context;
  9. import android.content.res.TypedArray;
  10. import android.graphics.Canvas;
  11. import android.graphics.Paint;
  12. import android.util.AttributeSet;
  13. import android.view.View;
  14. import android.view.ViewGroup;
  15. import de.tudarmstadt.informatik.hostage.R;
  16. public class FlowLayout extends ViewGroup {
  17. public static final int HORIZONTAL = 0;
  18. public static final int VERTICAL = 1;
  19. private int horizontalSpacing = 0;
  20. private int verticalSpacing = 0;
  21. private int orientation = 0;
  22. private boolean debugDraw = false;
  23. public FlowLayout(Context context) {
  24. super(context);
  25. this.readStyleParameters(context, null);
  26. }
  27. public FlowLayout(Context context, AttributeSet attributeSet) {
  28. super(context, attributeSet);
  29. this.readStyleParameters(context, attributeSet);
  30. }
  31. public FlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
  32. super(context, attributeSet, defStyle);
  33. this.readStyleParameters(context, attributeSet);
  34. }
  35. @Override
  36. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  37. int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - this.getPaddingRight() - this.getPaddingLeft();
  38. int sizeHeight = MeasureSpec.getSize(heightMeasureSpec) - this.getPaddingTop() - this.getPaddingBottom();
  39. int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
  40. int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
  41. int size;
  42. int mode;
  43. if (orientation == HORIZONTAL) {
  44. size = sizeWidth;
  45. mode = modeWidth;
  46. } else {
  47. size = sizeHeight;
  48. mode = modeHeight;
  49. }
  50. int lineThicknessWithSpacing = 0;
  51. int lineThickness = 0;
  52. int lineLengthWithSpacing = 0;
  53. int lineLength;
  54. int prevLinePosition = 0;
  55. int controlMaxLength = 0;
  56. int controlMaxThickness = 0;
  57. final int count = getChildCount();
  58. for (int i = 0; i < count; i++) {
  59. final View child = getChildAt(i);
  60. if (child.getVisibility() == GONE) {
  61. continue;
  62. }
  63. LayoutParams lp = (LayoutParams) child.getLayoutParams();
  64. child.measure(
  65. getChildMeasureSpec(widthMeasureSpec, this.getPaddingLeft() + this.getPaddingRight(), lp.width),
  66. getChildMeasureSpec(heightMeasureSpec, this.getPaddingTop() + this.getPaddingBottom(), lp.height)
  67. );
  68. int hSpacing = this.getHorizontalSpacing(lp);
  69. int vSpacing = this.getVerticalSpacing(lp);
  70. int childWidth = child.getMeasuredWidth();
  71. int childHeight = child.getMeasuredHeight();
  72. int childLength;
  73. int childThickness;
  74. int spacingLength;
  75. int spacingThickness;
  76. if (orientation == HORIZONTAL) {
  77. childLength = childWidth;
  78. childThickness = childHeight;
  79. spacingLength = hSpacing;
  80. spacingThickness = vSpacing;
  81. } else {
  82. childLength = childHeight;
  83. childThickness = childWidth;
  84. spacingLength = vSpacing;
  85. spacingThickness = hSpacing;
  86. }
  87. lineLength = lineLengthWithSpacing + childLength;
  88. lineLengthWithSpacing = lineLength + spacingLength;
  89. boolean newLine = lp.newLine || (mode != MeasureSpec.UNSPECIFIED && lineLength > size);
  90. if (newLine) {
  91. prevLinePosition = prevLinePosition + lineThicknessWithSpacing;
  92. lineThickness = childThickness;
  93. lineLength = childLength;
  94. lineThicknessWithSpacing = childThickness + spacingThickness;
  95. lineLengthWithSpacing = lineLength + spacingLength;
  96. }
  97. lineThicknessWithSpacing = Math.max(lineThicknessWithSpacing, childThickness + spacingThickness);
  98. lineThickness = Math.max(lineThickness, childThickness);
  99. int posX;
  100. int posY;
  101. if (orientation == HORIZONTAL) {
  102. posX = getPaddingLeft() + lineLength - childLength;
  103. posY = getPaddingTop() + prevLinePosition;
  104. } else {
  105. posX = getPaddingLeft() + prevLinePosition;
  106. posY = getPaddingTop() + lineLength - childHeight;
  107. }
  108. lp.setPosition(posX, posY);
  109. controlMaxLength = Math.max(controlMaxLength, lineLength);
  110. controlMaxThickness = prevLinePosition + lineThickness;
  111. }
  112. /* need to take paddings into account */
  113. if (orientation == HORIZONTAL) {
  114. controlMaxLength += getPaddingLeft() + getPaddingRight();
  115. controlMaxThickness += getPaddingBottom() + getPaddingTop();
  116. } else {
  117. controlMaxLength += getPaddingBottom() + getPaddingTop();
  118. controlMaxThickness += getPaddingLeft() + getPaddingRight();
  119. }
  120. if (orientation == HORIZONTAL) {
  121. this.setMeasuredDimension(resolveSize(controlMaxLength, widthMeasureSpec), resolveSize(controlMaxThickness, heightMeasureSpec));
  122. } else {
  123. this.setMeasuredDimension(resolveSize(controlMaxThickness, widthMeasureSpec), resolveSize(controlMaxLength, heightMeasureSpec));
  124. }
  125. }
  126. private int getVerticalSpacing(LayoutParams lp) {
  127. int vSpacing;
  128. if (lp.verticalSpacingSpecified()) {
  129. vSpacing = lp.verticalSpacing;
  130. } else {
  131. vSpacing = this.verticalSpacing;
  132. }
  133. return vSpacing;
  134. }
  135. private int getHorizontalSpacing(LayoutParams lp) {
  136. int hSpacing;
  137. if (lp.horizontalSpacingSpecified()) {
  138. hSpacing = lp.horizontalSpacing;
  139. } else {
  140. hSpacing = this.horizontalSpacing;
  141. }
  142. return hSpacing;
  143. }
  144. @Override
  145. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  146. final int count = getChildCount();
  147. for (int i = 0; i < count; i++) {
  148. View child = getChildAt(i);
  149. LayoutParams lp = (LayoutParams) child.getLayoutParams();
  150. child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
  151. }
  152. }
  153. @Override
  154. protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
  155. boolean more = super.drawChild(canvas, child, drawingTime);
  156. this.drawDebugInfo(canvas, child);
  157. return more;
  158. }
  159. @Override
  160. protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
  161. return p instanceof LayoutParams;
  162. }
  163. @Override
  164. protected LayoutParams generateDefaultLayoutParams() {
  165. return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  166. }
  167. @Override
  168. public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
  169. return new LayoutParams(getContext(), attributeSet);
  170. }
  171. @Override
  172. protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
  173. return new LayoutParams(p);
  174. }
  175. private void readStyleParameters(Context context, AttributeSet attributeSet) {
  176. TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.FlowLayout);
  177. try {
  178. horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
  179. verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
  180. orientation = a.getInteger(R.styleable.FlowLayout_orientation, HORIZONTAL);
  181. debugDraw = a.getBoolean(R.styleable.FlowLayout_debugDraw, false);
  182. } finally {
  183. a.recycle();
  184. }
  185. }
  186. private void drawDebugInfo(Canvas canvas, View child) {
  187. if (!debugDraw) {
  188. return;
  189. }
  190. Paint childPaint = this.createPaint(0xffffff00);
  191. Paint layoutPaint = this.createPaint(0xff00ff00);
  192. Paint newLinePaint = this.createPaint(0xffff0000);
  193. LayoutParams lp = (LayoutParams) child.getLayoutParams();
  194. if (lp.horizontalSpacing > 0) {
  195. float x = child.getRight();
  196. float y = child.getTop() + child.getHeight() / 2.0f;
  197. canvas.drawLine(x, y, x + lp.horizontalSpacing, y, childPaint);
  198. canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y - 4.0f, x + lp.horizontalSpacing, y, childPaint);
  199. canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y + 4.0f, x + lp.horizontalSpacing, y, childPaint);
  200. } else if (this.horizontalSpacing > 0) {
  201. float x = child.getRight();
  202. float y = child.getTop() + child.getHeight() / 2.0f;
  203. canvas.drawLine(x, y, x + this.horizontalSpacing, y, layoutPaint);
  204. canvas.drawLine(x + this.horizontalSpacing - 4.0f, y - 4.0f, x + this.horizontalSpacing, y, layoutPaint);
  205. canvas.drawLine(x + this.horizontalSpacing - 4.0f, y + 4.0f, x + this.horizontalSpacing, y, layoutPaint);
  206. }
  207. if (lp.verticalSpacing > 0) {
  208. float x = child.getLeft() + child.getWidth() / 2.0f;
  209. float y = child.getBottom();
  210. canvas.drawLine(x, y, x, y + lp.verticalSpacing, childPaint);
  211. canvas.drawLine(x - 4.0f, y + lp.verticalSpacing - 4.0f, x, y + lp.verticalSpacing, childPaint);
  212. canvas.drawLine(x + 4.0f, y + lp.verticalSpacing - 4.0f, x, y + lp.verticalSpacing, childPaint);
  213. } else if (this.verticalSpacing > 0) {
  214. float x = child.getLeft() + child.getWidth() / 2.0f;
  215. float y = child.getBottom();
  216. canvas.drawLine(x, y, x, y + this.verticalSpacing, layoutPaint);
  217. canvas.drawLine(x - 4.0f, y + this.verticalSpacing - 4.0f, x, y + this.verticalSpacing, layoutPaint);
  218. canvas.drawLine(x + 4.0f, y + this.verticalSpacing - 4.0f, x, y + this.verticalSpacing, layoutPaint);
  219. }
  220. if (lp.newLine) {
  221. if (orientation == HORIZONTAL) {
  222. float x = child.getLeft();
  223. float y = child.getTop() + child.getHeight() / 2.0f;
  224. canvas.drawLine(x, y - 6.0f, x, y + 6.0f, newLinePaint);
  225. } else {
  226. float x = child.getLeft() + child.getWidth() / 2.0f;
  227. float y = child.getTop();
  228. canvas.drawLine(x - 6.0f, y, x + 6.0f, y, newLinePaint);
  229. }
  230. }
  231. }
  232. private Paint createPaint(int color) {
  233. Paint paint = new Paint();
  234. paint.setAntiAlias(true);
  235. paint.setColor(color);
  236. paint.setStrokeWidth(2.0f);
  237. return paint;
  238. }
  239. public static class LayoutParams extends ViewGroup.LayoutParams {
  240. private static int NO_SPACING = -1;
  241. private int x;
  242. private int y;
  243. private int horizontalSpacing = NO_SPACING;
  244. private int verticalSpacing = NO_SPACING;
  245. private boolean newLine = false;
  246. public LayoutParams(Context context, AttributeSet attributeSet) {
  247. super(context, attributeSet);
  248. this.readStyleParameters(context, attributeSet);
  249. }
  250. public LayoutParams(int width, int height) {
  251. super(width, height);
  252. }
  253. public LayoutParams(ViewGroup.LayoutParams layoutParams) {
  254. super(layoutParams);
  255. }
  256. public boolean horizontalSpacingSpecified() {
  257. return horizontalSpacing != NO_SPACING;
  258. }
  259. public boolean verticalSpacingSpecified() {
  260. return verticalSpacing != NO_SPACING;
  261. }
  262. public void setPosition(int x, int y) {
  263. this.x = x;
  264. this.y = y;
  265. }
  266. private void readStyleParameters(Context context, AttributeSet attributeSet) {
  267. TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.FlowLayout_LayoutParams);
  268. try {
  269. horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing, NO_SPACING);
  270. verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_verticalSpacing, NO_SPACING);
  271. newLine = a.getBoolean(R.styleable.FlowLayout_LayoutParams_layout_newLine, false);
  272. } finally {
  273. a.recycle();
  274. }
  275. }
  276. }
  277. }