4947c41df55af233c098b25dc4d579f9f077be2b
[pub/Android/ownCloud.git] / actionbarsherlock / src / com / actionbarsherlock / internal / widget / IcsLinearLayout.java
1 package com.actionbarsherlock.internal.widget;
2
3 import android.content.Context;
4 import android.content.res.TypedArray;
5 import android.graphics.Canvas;
6 import android.graphics.drawable.Drawable;
7 import android.util.AttributeSet;
8 import android.view.View;
9 import android.widget.LinearLayout;
10
11 import com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout;
12
13 /**
14 * A simple extension of a regular linear layout that supports the divider API
15 * of Android 4.0+. The dividers are added adjacent to the children by changing
16 * their layout params. If you need to rely on the margins which fall in the
17 * same orientation as the layout you should wrap the child in a simple
18 * {@link android.widget.FrameLayout} so it can receive the margin.
19 */
20 public class IcsLinearLayout extends NineLinearLayout {
21 private static final int[] R_styleable_LinearLayout = new int[] {
22 /* 0 */ android.R.attr.divider,
23 /* 1 */ android.R.attr.measureWithLargestChild,
24 /* 2 */ android.R.attr.showDividers,
25 /* 3 */ android.R.attr.dividerPadding,
26 };
27 private static final int LinearLayout_divider = 0;
28 private static final int LinearLayout_measureWithLargestChild = 1;
29 private static final int LinearLayout_showDividers = 2;
30 private static final int LinearLayout_dividerPadding = 3;
31
32 /**
33 * Don't show any dividers.
34 */
35 public static final int SHOW_DIVIDER_NONE = 0;
36 /**
37 * Show a divider at the beginning of the group.
38 */
39 public static final int SHOW_DIVIDER_BEGINNING = 1;
40 /**
41 * Show dividers between each item in the group.
42 */
43 public static final int SHOW_DIVIDER_MIDDLE = 2;
44 /**
45 * Show a divider at the end of the group.
46 */
47 public static final int SHOW_DIVIDER_END = 4;
48
49
50 private Drawable mDivider;
51 private int mDividerWidth;
52 private int mDividerHeight;
53 private int mShowDividers;
54 private int mDividerPadding;
55
56 private boolean mUseLargestChild;
57
58 public IcsLinearLayout(Context context, AttributeSet attrs) {
59 super(context, attrs);
60
61 TypedArray a = context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/R_styleable_LinearLayout);
62
63 setDividerDrawable(a.getDrawable(/*com.android.internal.R.styleable.*/LinearLayout_divider));
64 mShowDividers = a.getInt(/*com.android.internal.R.styleable.*/LinearLayout_showDividers, SHOW_DIVIDER_NONE);
65 mDividerPadding = a.getDimensionPixelSize(/*com.android.internal.R.styleable.*/LinearLayout_dividerPadding, 0);
66 mUseLargestChild = a.getBoolean(/*com.android.internal.R.styleable.*/LinearLayout_measureWithLargestChild, false);
67
68 a.recycle();
69 }
70
71 /**
72 * Set how dividers should be shown between items in this layout
73 *
74 * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
75 * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
76 * or {@link #SHOW_DIVIDER_NONE} to show no dividers.
77 */
78 public void setShowDividers(int showDividers) {
79 if (showDividers != mShowDividers) {
80 requestLayout();
81 invalidate(); //XXX This is required if you are toggling a divider off
82 }
83 mShowDividers = showDividers;
84 }
85
86 /**
87 * @return A flag set indicating how dividers should be shown around items.
88 * @see #setShowDividers(int)
89 */
90 public int getShowDividers() {
91 return mShowDividers;
92 }
93
94 /**
95 * Set a drawable to be used as a divider between items.
96 * @param divider Drawable that will divide each item.
97 * @see #setShowDividers(int)
98 */
99 public void setDividerDrawable(Drawable divider) {
100 if (divider == mDivider) {
101 return;
102 }
103 mDivider = divider;
104 if (divider != null) {
105 mDividerWidth = divider.getIntrinsicWidth();
106 mDividerHeight = divider.getIntrinsicHeight();
107 } else {
108 mDividerWidth = 0;
109 mDividerHeight = 0;
110 }
111 setWillNotDraw(divider == null);
112 requestLayout();
113 }
114
115 /**
116 * Set padding displayed on both ends of dividers.
117 *
118 * @param padding Padding value in pixels that will be applied to each end
119 *
120 * @see #setShowDividers(int)
121 * @see #setDividerDrawable(Drawable)
122 * @see #getDividerPadding()
123 */
124 public void setDividerPadding(int padding) {
125 mDividerPadding = padding;
126 }
127
128 /**
129 * Get the padding size used to inset dividers in pixels
130 *
131 * @see #setShowDividers(int)
132 * @see #setDividerDrawable(Drawable)
133 * @see #setDividerPadding(int)
134 */
135 public int getDividerPadding() {
136 return mDividerPadding;
137 }
138
139 /**
140 * Get the width of the current divider drawable.
141 *
142 * @hide Used internally by framework.
143 */
144 public int getDividerWidth() {
145 return mDividerWidth;
146 }
147
148 @Override
149 protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
150 final int index = indexOfChild(child);
151 final int orientation = getOrientation();
152 final LayoutParams params = (LayoutParams) child.getLayoutParams();
153 if (hasDividerBeforeChildAt(index)) {
154 if (orientation == VERTICAL) {
155 //Account for the divider by pushing everything up
156 params.topMargin = mDividerHeight;
157 } else {
158 //Account for the divider by pushing everything left
159 params.leftMargin = mDividerWidth;
160 }
161 }
162
163 final int count = getChildCount();
164 if (index == count - 1) {
165 if (hasDividerBeforeChildAt(count)) {
166 if (orientation == VERTICAL) {
167 params.bottomMargin = mDividerHeight;
168 } else {
169 params.rightMargin = mDividerWidth;
170 }
171 }
172 }
173 super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
174 }
175
176 @Override
177 protected void onDraw(Canvas canvas) {
178 if (mDivider != null) {
179 if (getOrientation() == VERTICAL) {
180 drawDividersVertical(canvas);
181 } else {
182 drawDividersHorizontal(canvas);
183 }
184 }
185 super.onDraw(canvas);
186 }
187
188 void drawDividersVertical(Canvas canvas) {
189 final int count = getChildCount();
190 for (int i = 0; i < count; i++) {
191 final View child = getChildAt(i);
192
193 if (child != null && child.getVisibility() != GONE) {
194 if (hasDividerBeforeChildAt(i)) {
195 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
196 final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/;
197 drawHorizontalDivider(canvas, top);
198 }
199 }
200 }
201
202 if (hasDividerBeforeChildAt(count)) {
203 final View child = getChildAt(count - 1);
204 int bottom = 0;
205 if (child == null) {
206 bottom = getHeight() - getPaddingBottom() - mDividerHeight;
207 } else {
208 //final LayoutParams lp = (LayoutParams) child.getLayoutParams();
209 bottom = child.getBottom()/* + lp.bottomMargin*/;
210 }
211 drawHorizontalDivider(canvas, bottom);
212 }
213 }
214
215 void drawDividersHorizontal(Canvas canvas) {
216 final int count = getChildCount();
217 for (int i = 0; i < count; i++) {
218 final View child = getChildAt(i);
219
220 if (child != null && child.getVisibility() != GONE) {
221 if (hasDividerBeforeChildAt(i)) {
222 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
223 final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/;
224 drawVerticalDivider(canvas, left);
225 }
226 }
227 }
228
229 if (hasDividerBeforeChildAt(count)) {
230 final View child = getChildAt(count - 1);
231 int right = 0;
232 if (child == null) {
233 right = getWidth() - getPaddingRight() - mDividerWidth;
234 } else {
235 //final LayoutParams lp = (LayoutParams) child.getLayoutParams();
236 right = child.getRight()/* + lp.rightMargin*/;
237 }
238 drawVerticalDivider(canvas, right);
239 }
240 }
241
242 void drawHorizontalDivider(Canvas canvas, int top) {
243 mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
244 getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
245 mDivider.draw(canvas);
246 }
247
248 void drawVerticalDivider(Canvas canvas, int left) {
249 mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
250 left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
251 mDivider.draw(canvas);
252 }
253
254 /**
255 * Determines where to position dividers between children.
256 *
257 * @param childIndex Index of child to check for preceding divider
258 * @return true if there should be a divider before the child at childIndex
259 * @hide Pending API consideration. Currently only used internally by the system.
260 */
261 protected boolean hasDividerBeforeChildAt(int childIndex) {
262 if (childIndex == 0) {
263 return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
264 } else if (childIndex == getChildCount()) {
265 return (mShowDividers & SHOW_DIVIDER_END) != 0;
266 } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
267 boolean hasVisibleViewBefore = false;
268 for (int i = childIndex - 1; i >= 0; i--) {
269 if (getChildAt(i).getVisibility() != GONE) {
270 hasVisibleViewBefore = true;
271 break;
272 }
273 }
274 return hasVisibleViewBefore;
275 }
276 return false;
277 }
278
279 /**
280 * When true, all children with a weight will be considered having
281 * the minimum size of the largest child. If false, all children are
282 * measured normally.
283 *
284 * @return True to measure children with a weight using the minimum
285 * size of the largest child, false otherwise.
286 *
287 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
288 */
289 public boolean isMeasureWithLargestChildEnabled() {
290 return mUseLargestChild;
291 }
292
293 /**
294 * When set to true, all children with a weight will be considered having
295 * the minimum size of the largest child. If false, all children are
296 * measured normally.
297 *
298 * Disabled by default.
299 *
300 * @param enabled True to measure children with a weight using the
301 * minimum size of the largest child, false otherwise.
302 *
303 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
304 */
305 public void setMeasureWithLargestChildEnabled(boolean enabled) {
306 mUseLargestChild = enabled;
307 }
308
309 @Override
310 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
311 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
312
313 if (mUseLargestChild) {
314 final int orientation = getOrientation();
315 switch (orientation) {
316 case HORIZONTAL:
317 useLargestChildHorizontal();
318 break;
319
320 case VERTICAL:
321 useLargestChildVertical();
322 break;
323 }
324 }
325 }
326
327 private void useLargestChildHorizontal() {
328 final int childCount = getChildCount();
329
330 // Find largest child width
331 int largestChildWidth = 0;
332 for (int i = 0; i < childCount; i++) {
333 final View child = getChildAt(i);
334 largestChildWidth = Math.max(child.getMeasuredWidth(), largestChildWidth);
335 }
336
337 int totalWidth = 0;
338 // Re-measure childs
339 for (int i = 0; i < childCount; i++) {
340 final View child = getChildAt(i);
341
342 if (child == null || child.getVisibility() == View.GONE) {
343 continue;
344 }
345
346 final LinearLayout.LayoutParams lp =
347 (LinearLayout.LayoutParams) child.getLayoutParams();
348
349 float childExtra = lp.weight;
350 if (childExtra > 0) {
351 child.measure(
352 MeasureSpec.makeMeasureSpec(largestChildWidth,
353 MeasureSpec.EXACTLY),
354 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
355 MeasureSpec.EXACTLY));
356 totalWidth += largestChildWidth;
357
358 } else {
359 totalWidth += child.getMeasuredWidth();
360 }
361
362 totalWidth += lp.leftMargin + lp.rightMargin;
363 }
364
365 totalWidth += getPaddingLeft() + getPaddingRight();
366 setMeasuredDimension(totalWidth, getMeasuredHeight());
367 }
368
369 private void useLargestChildVertical() {
370 final int childCount = getChildCount();
371
372 // Find largest child width
373 int largestChildHeight = 0;
374 for (int i = 0; i < childCount; i++) {
375 final View child = getChildAt(i);
376 largestChildHeight = Math.max(child.getMeasuredHeight(), largestChildHeight);
377 }
378
379 int totalHeight = 0;
380 // Re-measure childs
381 for (int i = 0; i < childCount; i++) {
382 final View child = getChildAt(i);
383
384 if (child == null || child.getVisibility() == View.GONE) {
385 continue;
386 }
387
388 final LinearLayout.LayoutParams lp =
389 (LinearLayout.LayoutParams) child.getLayoutParams();
390
391 float childExtra = lp.weight;
392 if (childExtra > 0) {
393 child.measure(
394 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
395 MeasureSpec.EXACTLY),
396 MeasureSpec.makeMeasureSpec(largestChildHeight,
397 MeasureSpec.EXACTLY));
398 totalHeight += largestChildHeight;
399
400 } else {
401 totalHeight += child.getMeasuredHeight();
402 }
403
404 totalHeight += lp.leftMargin + lp.rightMargin;
405 }
406
407 totalHeight += getPaddingLeft() + getPaddingRight();
408 setMeasuredDimension(getMeasuredWidth(), totalHeight);
409 }
410 }