FlowLayout.java 11 KB

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