blob: 90692830842655c07afb3703276271d909faf330 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import com.android.internal.R;
20
svetoslavganov75986cf2009-05-14 22:28:01 -070021import android.content.Context;
22import android.content.res.TypedArray;
Romain Guy725015a2009-06-23 14:27:34 -070023import android.content.res.Resources;
svetoslavganov75986cf2009-05-14 22:28:01 -070024import android.graphics.Rect;
25import android.util.AttributeSet;
Romain Guy725015a2009-06-23 14:27:34 -070026import android.util.SparseArray;
27import android.util.Poolable;
28import android.util.Pool;
29import android.util.Pools;
30import android.util.PoolableManager;
31import static android.util.Log.d;
svetoslavganov75986cf2009-05-14 22:28:01 -070032import android.view.Gravity;
33import android.view.View;
34import android.view.ViewDebug;
35import android.view.ViewGroup;
36import android.view.accessibility.AccessibilityEvent;
37import android.widget.RemoteViews.RemoteView;
38
39import java.util.Comparator;
40import java.util.SortedSet;
41import java.util.TreeSet;
Romain Guy725015a2009-06-23 14:27:34 -070042import java.util.LinkedList;
Romain Guy725015a2009-06-23 14:27:34 -070043import java.util.HashSet;
Romain Guy1ab621e2009-06-25 13:31:57 -070044import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
46/**
47 * A Layout where the positions of the children can be described in relation to each other or to the
Romain Guy520c4202010-01-04 10:56:38 -080048 * parent.
Romain Guya1f3e4a2009-06-04 15:10:46 -070049 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 * <p>
51 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
52 * position of its children. For example, you cannot have a RelativeLayout whose height is set to
53 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
54 * {@link #ALIGN_PARENT_BOTTOM}.
55 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070056 *
Scott Main41ec6532010-08-19 16:57:07 -070057 * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-relativelayout.html">Relative
58 * Layout tutorial</a>.</p>
59 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 * <p>
61 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
62 * layout attributes
63 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070064 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 * @attr ref android.R.styleable#RelativeLayout_gravity
66 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
67 */
68@RemoteView
69public class RelativeLayout extends ViewGroup {
Romain Guy725015a2009-06-23 14:27:34 -070070 private static final String LOG_TAG = "RelativeLayout";
71
72 private static final boolean DEBUG_GRAPH = false;
73
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 public static final int TRUE = -1;
75
76 /**
77 * Rule that aligns a child's right edge with another child's left edge.
78 */
79 public static final int LEFT_OF = 0;
80 /**
81 * Rule that aligns a child's left edge with another child's right edge.
82 */
83 public static final int RIGHT_OF = 1;
84 /**
85 * Rule that aligns a child's bottom edge with another child's top edge.
86 */
87 public static final int ABOVE = 2;
88 /**
89 * Rule that aligns a child's top edge with another child's bottom edge.
90 */
91 public static final int BELOW = 3;
92
93 /**
94 * Rule that aligns a child's baseline with another child's baseline.
95 */
96 public static final int ALIGN_BASELINE = 4;
97 /**
98 * Rule that aligns a child's left edge with another child's left edge.
99 */
100 public static final int ALIGN_LEFT = 5;
101 /**
102 * Rule that aligns a child's top edge with another child's top edge.
103 */
104 public static final int ALIGN_TOP = 6;
105 /**
106 * Rule that aligns a child's right edge with another child's right edge.
107 */
108 public static final int ALIGN_RIGHT = 7;
109 /**
110 * Rule that aligns a child's bottom edge with another child's bottom edge.
111 */
112 public static final int ALIGN_BOTTOM = 8;
113
114 /**
115 * Rule that aligns the child's left edge with its RelativeLayout
116 * parent's left edge.
117 */
118 public static final int ALIGN_PARENT_LEFT = 9;
119 /**
120 * Rule that aligns the child's top edge with its RelativeLayout
121 * parent's top edge.
122 */
123 public static final int ALIGN_PARENT_TOP = 10;
124 /**
125 * Rule that aligns the child's right edge with its RelativeLayout
126 * parent's right edge.
127 */
128 public static final int ALIGN_PARENT_RIGHT = 11;
129 /**
130 * Rule that aligns the child's bottom edge with its RelativeLayout
131 * parent's bottom edge.
132 */
133 public static final int ALIGN_PARENT_BOTTOM = 12;
134
135 /**
136 * Rule that centers the child with respect to the bounds of its
137 * RelativeLayout parent.
138 */
139 public static final int CENTER_IN_PARENT = 13;
140 /**
141 * Rule that centers the child horizontally with respect to the
142 * bounds of its RelativeLayout parent.
143 */
144 public static final int CENTER_HORIZONTAL = 14;
145 /**
146 * Rule that centers the child vertically with respect to the
147 * bounds of its RelativeLayout parent.
148 */
149 public static final int CENTER_VERTICAL = 15;
150
151 private static final int VERB_COUNT = 16;
152
153 private View mBaselineView = null;
154 private boolean mHasBaselineAlignedChild;
155
156 private int mGravity = Gravity.LEFT | Gravity.TOP;
157 private final Rect mContentBounds = new Rect();
158 private final Rect mSelfBounds = new Rect();
159 private int mIgnoreGravity;
160
Romain Guy725015a2009-06-23 14:27:34 -0700161 private SortedSet<View> mTopToBottomLeftToRightSet = null;
162
163 private boolean mDirtyHierarchy;
164 private View[] mSortedHorizontalChildren = new View[0];
165 private View[] mSortedVerticalChildren = new View[0];
166 private final DependencyGraph mGraph = new DependencyGraph();
svetoslavganov75986cf2009-05-14 22:28:01 -0700167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 public RelativeLayout(Context context) {
169 super(context);
170 }
171
172 public RelativeLayout(Context context, AttributeSet attrs) {
173 super(context, attrs);
174 initFromAttributes(context, attrs);
175 }
176
177 public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
178 super(context, attrs, defStyle);
179 initFromAttributes(context, attrs);
180 }
181
182 private void initFromAttributes(Context context, AttributeSet attrs) {
183 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout);
184 mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
185 mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
186 a.recycle();
187 }
188
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700189 @Override
190 public boolean shouldDelayChildPressedState() {
191 return false;
192 }
193
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 /**
195 * Defines which View is ignored when the gravity is applied. This setting has no
196 * effect if the gravity is <code>Gravity.LEFT | Gravity.TOP</code>.
197 *
198 * @param viewId The id of the View to be ignored by gravity, or 0 if no View
199 * should be ignored.
200 *
201 * @see #setGravity(int)
202 *
203 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
204 */
205 @android.view.RemotableViewMethod
206 public void setIgnoreGravity(int viewId) {
207 mIgnoreGravity = viewId;
208 }
209
210 /**
211 * Describes how the child views are positioned. Defaults to
212 * <code>Gravity.LEFT | Gravity.TOP</code>.
213 *
214 * @param gravity See {@link android.view.Gravity}
215 *
216 * @see #setHorizontalGravity(int)
217 * @see #setVerticalGravity(int)
218 *
219 * @attr ref android.R.styleable#RelativeLayout_gravity
220 */
221 @android.view.RemotableViewMethod
222 public void setGravity(int gravity) {
223 if (mGravity != gravity) {
224 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
225 gravity |= Gravity.LEFT;
226 }
227
228 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
229 gravity |= Gravity.TOP;
230 }
231
232 mGravity = gravity;
233 requestLayout();
234 }
235 }
236
237 @android.view.RemotableViewMethod
238 public void setHorizontalGravity(int horizontalGravity) {
239 final int gravity = horizontalGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
240 if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) {
241 mGravity = (mGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) | gravity;
242 requestLayout();
243 }
244 }
245
246 @android.view.RemotableViewMethod
247 public void setVerticalGravity(int verticalGravity) {
248 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
249 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
250 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
251 requestLayout();
252 }
253 }
254
255 @Override
256 public int getBaseline() {
257 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
258 }
259
260 @Override
Romain Guy725015a2009-06-23 14:27:34 -0700261 public void requestLayout() {
262 super.requestLayout();
263 mDirtyHierarchy = true;
264 }
265
266 private void sortChildren() {
267 int count = getChildCount();
268 if (mSortedVerticalChildren.length != count) mSortedVerticalChildren = new View[count];
269 if (mSortedHorizontalChildren.length != count) mSortedHorizontalChildren = new View[count];
270
271 final DependencyGraph graph = mGraph;
272 graph.clear();
273
274 for (int i = 0; i < count; i++) {
275 final View child = getChildAt(i);
276 graph.add(child);
277 }
278
279 if (DEBUG_GRAPH) {
280 d(LOG_TAG, "=== Sorted vertical children");
281 graph.log(getResources(), ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM);
282 d(LOG_TAG, "=== Sorted horizontal children");
283 graph.log(getResources(), LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT);
284 }
285
286 graph.getSortedViews(mSortedVerticalChildren, ABOVE, BELOW, ALIGN_BASELINE,
287 ALIGN_TOP, ALIGN_BOTTOM);
288 graph.getSortedViews(mSortedHorizontalChildren, LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT);
Romain Guy9fffa1e2009-06-24 12:42:43 -0700289
290 if (DEBUG_GRAPH) {
291 d(LOG_TAG, "=== Ordered list of vertical children");
292 for (View view : mSortedVerticalChildren) {
293 DependencyGraph.printViewId(getResources(), view);
294 }
295 d(LOG_TAG, "=== Ordered list of horizontal children");
296 for (View view : mSortedHorizontalChildren) {
297 DependencyGraph.printViewId(getResources(), view);
298 }
299 }
Romain Guy725015a2009-06-23 14:27:34 -0700300 }
301
Romain Guy42460ac2010-01-11 16:46:33 -0800302 // TODO: we need to find another way to implement RelativeLayout
303 // This implementation cannot handle every case
Romain Guy725015a2009-06-23 14:27:34 -0700304 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Romain Guy725015a2009-06-23 14:27:34 -0700306 if (mDirtyHierarchy) {
307 mDirtyHierarchy = false;
308 sortChildren();
309 }
310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 int myWidth = -1;
312 int myHeight = -1;
313
314 int width = 0;
315 int height = 0;
316
317 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
318 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
319 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
320 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
321
322 // Record our dimensions if they are known;
323 if (widthMode != MeasureSpec.UNSPECIFIED) {
324 myWidth = widthSize;
325 }
326
327 if (heightMode != MeasureSpec.UNSPECIFIED) {
328 myHeight = heightSize;
329 }
330
331 if (widthMode == MeasureSpec.EXACTLY) {
332 width = myWidth;
333 }
334
335 if (heightMode == MeasureSpec.EXACTLY) {
336 height = myHeight;
337 }
338
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 mHasBaselineAlignedChild = false;
340
341 View ignore = null;
342 int gravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
343 final boolean horizontalGravity = gravity != Gravity.LEFT && gravity != 0;
344 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
345 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
346
347 int left = Integer.MAX_VALUE;
348 int top = Integer.MAX_VALUE;
349 int right = Integer.MIN_VALUE;
350 int bottom = Integer.MIN_VALUE;
351
Romain Guyf7dabb02009-06-25 14:47:14 -0700352 boolean offsetHorizontalAxis = false;
353 boolean offsetVerticalAxis = false;
354
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
356 ignore = findViewById(mIgnoreGravity);
357 }
358
Romain Guyf7dabb02009-06-25 14:47:14 -0700359 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
360 final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
361
Romain Guye24ef602009-06-25 13:01:55 -0700362 View[] views = mSortedHorizontalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700363 int count = views.length;
364 for (int i = 0; i < count; i++) {
365 View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 if (child.getVisibility() != GONE) {
367 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guy95607032009-06-24 14:37:03 -0700368
369 applyHorizontalSizeRules(params, myWidth);
Romain Guyf782e602009-06-25 15:26:49 -0700370 measureChildHorizontal(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700371 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
372 offsetHorizontalAxis = true;
373 }
Romain Guy725015a2009-06-23 14:27:34 -0700374 }
375 }
376
Romain Guye24ef602009-06-25 13:01:55 -0700377 views = mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700378 count = views.length;
Romain Guyf7dabb02009-06-25 14:47:14 -0700379
Romain Guy725015a2009-06-23 14:27:34 -0700380 for (int i = 0; i < count; i++) {
381 View child = views[i];
382 if (child.getVisibility() != GONE) {
383 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guy95607032009-06-24 14:37:03 -0700384
385 applyVerticalSizeRules(params, myHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 measureChild(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700387 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
388 offsetVerticalAxis = true;
389 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390
Romain Guyf7dabb02009-06-25 14:47:14 -0700391 if (isWrapContentWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 width = Math.max(width, params.mRight);
393 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700394
395 if (isWrapContentHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 height = Math.max(height, params.mBottom);
397 }
398
399 if (child != ignore || verticalGravity) {
400 left = Math.min(left, params.mLeft - params.leftMargin);
401 top = Math.min(top, params.mTop - params.topMargin);
402 }
403
404 if (child != ignore || horizontalGravity) {
405 right = Math.max(right, params.mRight + params.rightMargin);
406 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
407 }
408 }
409 }
410
411 if (mHasBaselineAlignedChild) {
Romain Guy725015a2009-06-23 14:27:34 -0700412 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 View child = getChildAt(i);
414 if (child.getVisibility() != GONE) {
415 LayoutParams params = (LayoutParams) child.getLayoutParams();
416 alignBaseline(child, params);
417
418 if (child != ignore || verticalGravity) {
Romain Guy725015a2009-06-23 14:27:34 -0700419 left = Math.min(left, params.mLeft - params.leftMargin);
420 top = Math.min(top, params.mTop - params.topMargin);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 }
422
423 if (child != ignore || horizontalGravity) {
424 right = Math.max(right, params.mRight + params.rightMargin);
425 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
426 }
427 }
428 }
429 }
430
Romain Guyf7dabb02009-06-25 14:47:14 -0700431 if (isWrapContentWidth) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700432 // Width already has left padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 // the right of each child view
434 width += mPaddingRight;
435
436 if (mLayoutParams.width >= 0) {
437 width = Math.max(width, mLayoutParams.width);
438 }
439
440 width = Math.max(width, getSuggestedMinimumWidth());
441 width = resolveSize(width, widthMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700442
443 if (offsetHorizontalAxis) {
Romain Guyd10a5762009-07-28 11:18:14 -0700444 for (int i = 0; i < count; i++) {
Romain Guyf7dabb02009-06-25 14:47:14 -0700445 View child = getChildAt(i);
446 if (child.getVisibility() != GONE) {
447 LayoutParams params = (LayoutParams) child.getLayoutParams();
448 final int[] rules = params.getRules();
449 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
450 centerHorizontal(child, params, width);
Romain Guy42460ac2010-01-11 16:46:33 -0800451 } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
452 final int childWidth = child.getMeasuredWidth();
453 params.mLeft = width - mPaddingRight - childWidth;
454 params.mRight = params.mLeft + childWidth;
Romain Guyf7dabb02009-06-25 14:47:14 -0700455 }
456 }
457 }
458 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700460
461 if (isWrapContentHeight) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700462 // Height already has top padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 // the bottom of each child view
464 height += mPaddingBottom;
465
466 if (mLayoutParams.height >= 0) {
467 height = Math.max(height, mLayoutParams.height);
468 }
469
470 height = Math.max(height, getSuggestedMinimumHeight());
471 height = resolveSize(height, heightMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700472
473 if (offsetVerticalAxis) {
474 for (int i = 0; i < count; i++) {
475 View child = getChildAt(i);
476 if (child.getVisibility() != GONE) {
477 LayoutParams params = (LayoutParams) child.getLayoutParams();
478 final int[] rules = params.getRules();
479 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
480 centerVertical(child, params, height);
Romain Guy42460ac2010-01-11 16:46:33 -0800481 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
482 final int childHeight = child.getMeasuredHeight();
483 params.mTop = height - mPaddingBottom - childHeight;
484 params.mBottom = params.mTop + childHeight;
Romain Guyf7dabb02009-06-25 14:47:14 -0700485 }
486 }
487 }
488 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 }
490
491 if (horizontalGravity || verticalGravity) {
492 final Rect selfBounds = mSelfBounds;
493 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
494 height - mPaddingBottom);
495
496 final Rect contentBounds = mContentBounds;
497 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds);
498
499 final int horizontalOffset = contentBounds.left - left;
500 final int verticalOffset = contentBounds.top - top;
501 if (horizontalOffset != 0 || verticalOffset != 0) {
Romain Guy725015a2009-06-23 14:27:34 -0700502 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 View child = getChildAt(i);
504 if (child.getVisibility() != GONE && child != ignore) {
505 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guyd10a5762009-07-28 11:18:14 -0700506 if (horizontalGravity) {
507 params.mLeft += horizontalOffset;
508 params.mRight += horizontalOffset;
509 }
510 if (verticalGravity) {
511 params.mTop += verticalOffset;
512 params.mBottom += verticalOffset;
513 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 }
515 }
516 }
517 }
518
519 setMeasuredDimension(width, height);
520 }
521
522 private void alignBaseline(View child, LayoutParams params) {
523 int[] rules = params.getRules();
524 int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE);
525
526 if (anchorBaseline != -1) {
527 LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE);
528 if (anchorParams != null) {
529 int offset = anchorParams.mTop + anchorBaseline;
530 int baseline = child.getBaseline();
531 if (baseline != -1) {
532 offset -= baseline;
533 }
534 int height = params.mBottom - params.mTop;
535 params.mTop = offset;
536 params.mBottom = params.mTop + height;
537 }
538 }
539
540 if (mBaselineView == null) {
541 mBaselineView = child;
542 } else {
543 LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams();
544 if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) {
545 mBaselineView = child;
546 }
547 }
548 }
549
550 /**
551 * Measure a child. The child should have left, top, right and bottom information
552 * stored in its LayoutParams. If any of these values is -1 it means that the view
553 * can extend up to the corresponding edge.
554 *
555 * @param child Child to measure
556 * @param params LayoutParams associated with child
557 * @param myWidth Width of the the RelativeLayout
558 * @param myHeight Height of the RelativeLayout
559 */
Romain Guy725015a2009-06-23 14:27:34 -0700560 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
562 params.mRight, params.width,
563 params.leftMargin, params.rightMargin,
564 mPaddingLeft, mPaddingRight,
565 myWidth);
566 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
567 params.mBottom, params.height,
568 params.topMargin, params.bottomMargin,
569 mPaddingTop, mPaddingBottom,
570 myHeight);
571 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
572 }
573
Romain Guyf782e602009-06-25 15:26:49 -0700574 private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
Romain Guy95607032009-06-24 14:37:03 -0700575 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
576 params.mRight, params.width,
577 params.leftMargin, params.rightMargin,
578 mPaddingLeft, mPaddingRight,
579 myWidth);
Romain Guyf782e602009-06-25 15:26:49 -0700580 int childHeightMeasureSpec;
Romain Guy980a9382010-01-08 15:06:28 -0800581 if (params.width == LayoutParams.MATCH_PARENT) {
Romain Guybaac4632009-06-29 14:28:29 -0700582 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
Romain Guyf782e602009-06-25 15:26:49 -0700583 } else {
Romain Guybaac4632009-06-29 14:28:29 -0700584 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
Romain Guyf782e602009-06-25 15:26:49 -0700585 }
Romain Guy725015a2009-06-23 14:27:34 -0700586 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
587 }
588
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 /**
590 * Get a measure spec that accounts for all of the constraints on this view.
591 * This includes size contstraints imposed by the RelativeLayout as well as
592 * the View's desired dimension.
593 *
594 * @param childStart The left or top field of the child's layout params
595 * @param childEnd The right or bottom field of the child's layout params
596 * @param childSize The child's desired size (the width or height field of
597 * the child's layout params)
598 * @param startMargin The left or top margin
599 * @param endMargin The right or bottom margin
600 * @param startPadding mPaddingLeft or mPaddingTop
601 * @param endPadding mPaddingRight or mPaddingBottom
602 * @param mySize The width or height of this view (the RelativeLayout)
603 * @return MeasureSpec for the child
604 */
605 private int getChildMeasureSpec(int childStart, int childEnd,
606 int childSize, int startMargin, int endMargin, int startPadding,
607 int endPadding, int mySize) {
608 int childSpecMode = 0;
609 int childSpecSize = 0;
610
611 // Figure out start and end bounds.
612 int tempStart = childStart;
613 int tempEnd = childEnd;
614
615 // If the view did not express a layout constraint for an edge, use
616 // view's margins and our padding
617 if (tempStart < 0) {
618 tempStart = startPadding + startMargin;
619 }
620 if (tempEnd < 0) {
621 tempEnd = mySize - endPadding - endMargin;
622 }
623
624 // Figure out maximum size available to this view
625 int maxAvailable = tempEnd - tempStart;
626
627 if (childStart >= 0 && childEnd >= 0) {
628 // Constraints fixed both edges, so child must be an exact size
629 childSpecMode = MeasureSpec.EXACTLY;
630 childSpecSize = maxAvailable;
631 } else {
632 if (childSize >= 0) {
633 // Child wanted an exact size. Give as much as possible
634 childSpecMode = MeasureSpec.EXACTLY;
635
636 if (maxAvailable >= 0) {
637 // We have a maxmum size in this dimension.
638 childSpecSize = Math.min(maxAvailable, childSize);
639 } else {
640 // We can grow in this dimension.
641 childSpecSize = childSize;
642 }
Romain Guy980a9382010-01-08 15:06:28 -0800643 } else if (childSize == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 // Child wanted to be as big as possible. Give all availble
645 // space
646 childSpecMode = MeasureSpec.EXACTLY;
647 childSpecSize = maxAvailable;
648 } else if (childSize == LayoutParams.WRAP_CONTENT) {
649 // Child wants to wrap content. Use AT_MOST
650 // to communicate available space if we know
651 // our max size
652 if (maxAvailable >= 0) {
653 // We have a maxmum size in this dimension.
654 childSpecMode = MeasureSpec.AT_MOST;
655 childSpecSize = maxAvailable;
656 } else {
657 // We can grow in this dimension. Child can be as big as it
658 // wants
659 childSpecMode = MeasureSpec.UNSPECIFIED;
660 childSpecSize = 0;
661 }
662 }
663 }
664
665 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
666 }
667
Romain Guyf7dabb02009-06-25 14:47:14 -0700668 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
669 boolean wrapContent) {
670
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 int[] rules = params.getRules();
672
673 if (params.mLeft < 0 && params.mRight >= 0) {
674 // Right is fixed, but left varies
675 params.mLeft = params.mRight - child.getMeasuredWidth();
676 } else if (params.mLeft >= 0 && params.mRight < 0) {
677 // Left is fixed, but right varies
678 params.mRight = params.mLeft + child.getMeasuredWidth();
679 } else if (params.mLeft < 0 && params.mRight < 0) {
680 // Both left and right vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700681 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
682 if (!wrapContent) {
683 centerHorizontal(child, params, myWidth);
684 } else {
685 params.mLeft = mPaddingLeft + params.leftMargin;
686 params.mRight = params.mLeft + child.getMeasuredWidth();
687 }
688 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 } else {
690 params.mLeft = mPaddingLeft + params.leftMargin;
691 params.mRight = params.mLeft + child.getMeasuredWidth();
692 }
693 }
Romain Guy42460ac2010-01-11 16:46:33 -0800694 return rules[ALIGN_PARENT_RIGHT] != 0;
Romain Guy725015a2009-06-23 14:27:34 -0700695 }
696
Romain Guyf7dabb02009-06-25 14:47:14 -0700697 private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
698 boolean wrapContent) {
699
Romain Guy725015a2009-06-23 14:27:34 -0700700 int[] rules = params.getRules();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701
702 if (params.mTop < 0 && params.mBottom >= 0) {
703 // Bottom is fixed, but top varies
704 params.mTop = params.mBottom - child.getMeasuredHeight();
705 } else if (params.mTop >= 0 && params.mBottom < 0) {
706 // Top is fixed, but bottom varies
707 params.mBottom = params.mTop + child.getMeasuredHeight();
708 } else if (params.mTop < 0 && params.mBottom < 0) {
709 // Both top and bottom vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700710 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
711 if (!wrapContent) {
712 centerVertical(child, params, myHeight);
713 } else {
714 params.mTop = mPaddingTop + params.topMargin;
715 params.mBottom = params.mTop + child.getMeasuredHeight();
716 }
717 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 } else {
719 params.mTop = mPaddingTop + params.topMargin;
720 params.mBottom = params.mTop + child.getMeasuredHeight();
721 }
722 }
Romain Guy42460ac2010-01-11 16:46:33 -0800723 return rules[ALIGN_PARENT_BOTTOM] != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 }
725
Romain Guy725015a2009-06-23 14:27:34 -0700726 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 int[] rules = childParams.getRules();
728 RelativeLayout.LayoutParams anchorParams;
729
730 // -1 indicated a "soft requirement" in that direction. For example:
731 // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right
732 // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left
733 // left=10, right=20 means the left and right ends are both fixed
734 childParams.mLeft = -1;
735 childParams.mRight = -1;
736
737 anchorParams = getRelatedViewParams(rules, LEFT_OF);
738 if (anchorParams != null) {
739 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
740 childParams.rightMargin);
741 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
742 if (myWidth >= 0) {
743 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
744 } else {
745 // FIXME uh oh...
746 }
747 }
748
749 anchorParams = getRelatedViewParams(rules, RIGHT_OF);
750 if (anchorParams != null) {
751 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
752 childParams.leftMargin);
753 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
754 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
755 }
756
757 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
758 if (anchorParams != null) {
759 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
760 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
761 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
762 }
763
764 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
765 if (anchorParams != null) {
766 childParams.mRight = anchorParams.mRight - childParams.rightMargin;
767 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
768 if (myWidth >= 0) {
769 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
770 } else {
771 // FIXME uh oh...
772 }
773 }
774
775 if (0 != rules[ALIGN_PARENT_LEFT]) {
776 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
777 }
778
779 if (0 != rules[ALIGN_PARENT_RIGHT]) {
780 if (myWidth >= 0) {
781 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
782 } else {
783 // FIXME uh oh...
784 }
785 }
Romain Guy725015a2009-06-23 14:27:34 -0700786 }
787
788 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) {
789 int[] rules = childParams.getRules();
790 RelativeLayout.LayoutParams anchorParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791
792 childParams.mTop = -1;
793 childParams.mBottom = -1;
794
795 anchorParams = getRelatedViewParams(rules, ABOVE);
796 if (anchorParams != null) {
797 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
798 childParams.bottomMargin);
799 } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
800 if (myHeight >= 0) {
801 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
802 } else {
803 // FIXME uh oh...
804 }
805 }
806
807 anchorParams = getRelatedViewParams(rules, BELOW);
808 if (anchorParams != null) {
809 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
810 childParams.topMargin);
811 } else if (childParams.alignWithParent && rules[BELOW] != 0) {
812 childParams.mTop = mPaddingTop + childParams.topMargin;
813 }
814
815 anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
816 if (anchorParams != null) {
817 childParams.mTop = anchorParams.mTop + childParams.topMargin;
818 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
819 childParams.mTop = mPaddingTop + childParams.topMargin;
820 }
821
822 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
823 if (anchorParams != null) {
824 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
825 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
826 if (myHeight >= 0) {
827 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
828 } else {
829 // FIXME uh oh...
830 }
831 }
832
833 if (0 != rules[ALIGN_PARENT_TOP]) {
834 childParams.mTop = mPaddingTop + childParams.topMargin;
835 }
836
837 if (0 != rules[ALIGN_PARENT_BOTTOM]) {
838 if (myHeight >= 0) {
839 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
840 } else {
841 // FIXME uh oh...
842 }
843 }
844
845 if (rules[ALIGN_BASELINE] != 0) {
846 mHasBaselineAlignedChild = true;
847 }
848 }
849
850 private View getRelatedView(int[] rules, int relation) {
851 int id = rules[relation];
852 if (id != 0) {
Romain Guy1ab621e2009-06-25 13:31:57 -0700853 DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
Romain Guya0fd1d72009-06-24 14:25:43 -0700854 if (node == null) return null;
855 View v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856
857 // Find the first non-GONE view up the chain
858 while (v.getVisibility() == View.GONE) {
859 rules = ((LayoutParams) v.getLayoutParams()).getRules();
Romain Guy1ab621e2009-06-25 13:31:57 -0700860 node = mGraph.mKeyNodes.get((rules[relation]));
Romain Guya0fd1d72009-06-24 14:25:43 -0700861 if (node == null) return null;
862 v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 }
864
865 return v;
866 }
867
868 return null;
869 }
870
871 private LayoutParams getRelatedViewParams(int[] rules, int relation) {
872 View v = getRelatedView(rules, relation);
873 if (v != null) {
874 ViewGroup.LayoutParams params = v.getLayoutParams();
875 if (params instanceof LayoutParams) {
876 return (LayoutParams) v.getLayoutParams();
877 }
878 }
879 return null;
880 }
881
882 private int getRelatedViewBaseline(int[] rules, int relation) {
883 View v = getRelatedView(rules, relation);
884 if (v != null) {
885 return v.getBaseline();
886 }
887 return -1;
888 }
889
890 private void centerHorizontal(View child, LayoutParams params, int myWidth) {
891 int childWidth = child.getMeasuredWidth();
892 int left = (myWidth - childWidth) / 2;
893
894 params.mLeft = left;
895 params.mRight = left + childWidth;
896 }
897
898 private void centerVertical(View child, LayoutParams params, int myHeight) {
899 int childHeight = child.getMeasuredHeight();
900 int top = (myHeight - childHeight) / 2;
901
902 params.mTop = top;
903 params.mBottom = top + childHeight;
904 }
905
906 @Override
907 protected void onLayout(boolean changed, int l, int t, int r, int b) {
908 // The layout has actually already been performed and the positions
909 // cached. Apply the cached values to the children.
910 int count = getChildCount();
911
912 for (int i = 0; i < count; i++) {
913 View child = getChildAt(i);
914 if (child.getVisibility() != GONE) {
915 RelativeLayout.LayoutParams st =
916 (RelativeLayout.LayoutParams) child.getLayoutParams();
917 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
918
919 }
920 }
921 }
922
923 @Override
924 public LayoutParams generateLayoutParams(AttributeSet attrs) {
925 return new RelativeLayout.LayoutParams(getContext(), attrs);
926 }
927
928 /**
929 * Returns a set of layout parameters with a width of
930 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
931 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
932 */
933 @Override
934 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
935 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
936 }
937
938 // Override to allow type-checking of LayoutParams.
939 @Override
940 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
941 return p instanceof RelativeLayout.LayoutParams;
942 }
943
944 @Override
945 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
946 return new LayoutParams(p);
947 }
948
svetoslavganov75986cf2009-05-14 22:28:01 -0700949 @Override
950 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
951 if (mTopToBottomLeftToRightSet == null) {
952 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
953 }
954
955 // sort children top-to-bottom and left-to-right
956 for (int i = 0, count = getChildCount(); i < count; i++) {
957 mTopToBottomLeftToRightSet.add(getChildAt(i));
958 }
959
960 for (View view : mTopToBottomLeftToRightSet) {
961 if (view.dispatchPopulateAccessibilityEvent(event)) {
962 mTopToBottomLeftToRightSet.clear();
963 return true;
964 }
965 }
966
967 mTopToBottomLeftToRightSet.clear();
968 return false;
969 }
970
971 /**
972 * Compares two views in left-to-right and top-to-bottom fashion.
973 */
974 private class TopToBottomLeftToRightComparator implements Comparator<View> {
975 public int compare(View first, View second) {
976 // top - bottom
977 int topDifference = first.getTop() - second.getTop();
978 if (topDifference != 0) {
979 return topDifference;
980 }
981 // left - right
982 int leftDifference = first.getLeft() - second.getLeft();
983 if (leftDifference != 0) {
984 return leftDifference;
985 }
986 // break tie by height
987 int heightDiference = first.getHeight() - second.getHeight();
988 if (heightDiference != 0) {
989 return heightDiference;
990 }
991 // break tie by width
992 int widthDiference = first.getWidth() - second.getWidth();
993 if (widthDiference != 0) {
994 return widthDiference;
995 }
996 return 0;
997 }
998 }
999
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 /**
1001 * Per-child layout information associated with RelativeLayout.
1002 *
1003 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1004 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1005 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1006 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1007 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1008 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1009 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1010 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1011 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1012 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1013 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1014 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1015 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1016 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1017 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1018 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1019 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
1020 */
1021 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001022 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001023 @ViewDebug.IntToString(from = ABOVE, to = "above"),
1024 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
1025 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
1026 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"),
1027 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1028 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"),
1029 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"),
1030 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"),
1031 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"),
1032 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"),
1033 @ViewDebug.IntToString(from = BELOW, to = "below"),
1034 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"),
1035 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"),
1036 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"),
1037 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"),
1038 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf")
The Android Open Source Project10592532009-03-18 17:39:46 -07001039 }, mapping = {
1040 @ViewDebug.IntToString(from = TRUE, to = "true"),
Romain Guya1f3e4a2009-06-04 15:10:46 -07001041 @ViewDebug.IntToString(from = 0, to = "false/NO_ID")
The Android Open Source Project10592532009-03-18 17:39:46 -07001042 })
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 private int[] mRules = new int[VERB_COUNT];
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001044
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 private int mLeft, mTop, mRight, mBottom;
1046
1047 /**
1048 * When true, uses the parent as the anchor if the anchor doesn't exist or if
1049 * the anchor's visibility is GONE.
1050 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001051 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 public boolean alignWithParent;
1053
1054 public LayoutParams(Context c, AttributeSet attrs) {
1055 super(c, attrs);
1056
1057 TypedArray a = c.obtainStyledAttributes(attrs,
1058 com.android.internal.R.styleable.RelativeLayout_Layout);
1059
1060 final int[] rules = mRules;
1061
1062 final int N = a.getIndexCount();
1063 for (int i = 0; i < N; i++) {
1064 int attr = a.getIndex(i);
1065 switch (attr) {
1066 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1067 alignWithParent = a.getBoolean(attr, false);
1068 break;
1069 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1070 rules[LEFT_OF] = a.getResourceId(attr, 0);
1071 break;
1072 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1073 rules[RIGHT_OF] = a.getResourceId(attr, 0);
1074 break;
1075 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1076 rules[ABOVE] = a.getResourceId(attr, 0);
1077 break;
1078 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1079 rules[BELOW] = a.getResourceId(attr, 0);
1080 break;
1081 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1082 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1083 break;
1084 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1085 rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1086 break;
1087 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1088 rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1089 break;
1090 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1091 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1092 break;
1093 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1094 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1095 break;
1096 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1097 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1098 break;
1099 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1100 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1101 break;
1102 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1103 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1104 break;
1105 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1106 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1107 break;
1108 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1109 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1110 break;
1111 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1112 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1113 break;
1114 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1115 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1116 break;
1117 }
1118 }
1119
1120 a.recycle();
1121 }
1122
1123 public LayoutParams(int w, int h) {
1124 super(w, h);
1125 }
1126
1127 /**
1128 * {@inheritDoc}
1129 */
1130 public LayoutParams(ViewGroup.LayoutParams source) {
1131 super(source);
1132 }
1133
1134 /**
1135 * {@inheritDoc}
1136 */
1137 public LayoutParams(ViewGroup.MarginLayoutParams source) {
1138 super(source);
1139 }
1140
1141 @Override
1142 public String debug(String output) {
1143 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1144 ", height=" + sizeToString(height) + " }";
1145 }
1146
1147 /**
1148 * Adds a layout rule to be interpreted by the RelativeLayout. This
1149 * method should only be used for constraints that don't refer to another sibling
1150 * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE}
1151 * for true or - for false). To specify a verb that takes a subject, use
1152 * {@link #addRule(int, int)} instead.
1153 *
1154 * @param verb One of the verbs defined by
1155 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1156 * ALIGN_WITH_PARENT_LEFT.
1157 * @see #addRule(int, int)
1158 */
1159 public void addRule(int verb) {
1160 mRules[verb] = TRUE;
1161 }
1162
1163 /**
1164 * Adds a layout rule to be interpreted by the RelativeLayout. Use this for
1165 * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
1166 * value (VISIBLE).
1167 *
1168 * @param verb One of the verbs defined by
1169 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1170 * ALIGN_WITH_PARENT_LEFT.
1171 * @param anchor The id of another view to use as an anchor,
1172 * or a boolean value(represented as {@link RelativeLayout#TRUE})
1173 * for true or 0 for false). For verbs that don't refer to another sibling
1174 * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
1175 * @see #addRule(int)
1176 */
1177 public void addRule(int verb, int anchor) {
1178 mRules[verb] = anchor;
1179 }
1180
1181 /**
1182 * Retrieves a complete list of all supported rules, where the index is the rule
1183 * verb, and the element value is the value specified, or "false" if it was never
1184 * set.
1185 *
1186 * @return the supported rules
1187 * @see #addRule(int, int)
1188 */
1189 public int[] getRules() {
1190 return mRules;
1191 }
1192 }
Romain Guy725015a2009-06-23 14:27:34 -07001193
1194 private static class DependencyGraph {
1195 /**
Romain Guy1ab621e2009-06-25 13:31:57 -07001196 * List of all views in the graph.
1197 */
1198 private ArrayList<Node> mNodes = new ArrayList<Node>();
1199
1200 /**
Romain Guy725015a2009-06-23 14:27:34 -07001201 * List of nodes in the graph. Each node is identified by its
1202 * view id (see View#getId()).
1203 */
Romain Guy1ab621e2009-06-25 13:31:57 -07001204 private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001205
1206 /**
1207 * Temporary data structure used to build the list of roots
1208 * for this graph.
1209 */
1210 private LinkedList<Node> mRoots = new LinkedList<Node>();
1211
1212 /**
1213 * Clears the graph.
1214 */
1215 void clear() {
Romain Guy1ab621e2009-06-25 13:31:57 -07001216 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001217 final int count = nodes.size();
1218
1219 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001220 nodes.get(i).release();
Romain Guy725015a2009-06-23 14:27:34 -07001221 }
1222 nodes.clear();
1223
Romain Guy1ab621e2009-06-25 13:31:57 -07001224 mKeyNodes.clear();
Romain Guy725015a2009-06-23 14:27:34 -07001225 mRoots.clear();
1226 }
1227
1228 /**
1229 * Adds a view to the graph.
1230 *
1231 * @param view The view to be added as a node to the graph.
1232 */
1233 void add(View view) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001234 final int id = view.getId();
1235 final Node node = Node.acquire(view);
1236
1237 if (id != View.NO_ID) {
1238 mKeyNodes.put(id, node);
1239 }
1240
1241 mNodes.add(node);
Romain Guy725015a2009-06-23 14:27:34 -07001242 }
1243
1244 /**
1245 * Builds a sorted list of views. The sorting order depends on the dependencies
1246 * between the view. For instance, if view C needs view A to be processed first
1247 * and view A needs view B to be processed first, the dependency graph
1248 * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1249 *
1250 * @param sorted The sorted list of views. The length of this array must
1251 * be equal to getChildCount().
1252 * @param rules The list of rules to take into account.
1253 */
1254 void getSortedViews(View[] sorted, int... rules) {
1255 final LinkedList<Node> roots = findRoots(rules);
1256 int index = 0;
1257
Romain Guy725015a2009-06-23 14:27:34 -07001258 while (roots.size() > 0) {
1259 final Node node = roots.removeFirst();
1260 final View view = node.view;
1261 final int key = view.getId();
1262
1263 sorted[index++] = view;
1264
1265 final HashSet<Node> dependents = node.dependents;
1266 for (Node dependent : dependents) {
1267 final SparseArray<Node> dependencies = dependent.dependencies;
1268
1269 dependencies.remove(key);
1270 if (dependencies.size() == 0) {
1271 roots.add(dependent);
1272 }
1273 }
1274 }
1275
1276 if (index < sorted.length) {
1277 throw new IllegalStateException("Circular dependencies cannot exist"
1278 + " in RelativeLayout");
1279 }
1280 }
1281
1282 /**
1283 * Finds the roots of the graph. A root is a node with no dependency and
1284 * with [0..n] dependents.
1285 *
1286 * @param rulesFilter The list of rules to consider when building the
1287 * dependencies
1288 *
1289 * @return A list of node, each being a root of the graph
1290 */
1291 private LinkedList<Node> findRoots(int[] rulesFilter) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001292 final SparseArray<Node> keyNodes = mKeyNodes;
1293 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001294 final int count = nodes.size();
1295
1296 // Find roots can be invoked several times, so make sure to clear
1297 // all dependents and dependencies before running the algorithm
1298 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001299 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001300 node.dependents.clear();
1301 node.dependencies.clear();
1302 }
1303
1304 // Builds up the dependents and dependencies for each node of the graph
1305 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001306 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001307
1308 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1309 final int[] rules = layoutParams.mRules;
1310 final int rulesCount = rulesFilter.length;
1311
1312 // Look only the the rules passed in parameter, this way we build only the
1313 // dependencies for a specific set of rules
1314 for (int j = 0; j < rulesCount; j++) {
1315 final int rule = rules[rulesFilter[j]];
1316 if (rule > 0) {
1317 // The node this node depends on
Romain Guy1ab621e2009-06-25 13:31:57 -07001318 final Node dependency = keyNodes.get(rule);
Romain Guyda3003e2009-07-19 19:47:42 -07001319 // Skip unknowns and self dependencies
1320 if (dependency == null || dependency == node) {
Romain Guyb8f8de82009-06-25 12:03:56 -07001321 continue;
1322 }
Romain Guy725015a2009-06-23 14:27:34 -07001323 // Add the current node as a dependent
1324 dependency.dependents.add(node);
1325 // Add a dependency to the current node
1326 node.dependencies.put(rule, dependency);
1327 }
1328 }
1329 }
1330
1331 final LinkedList<Node> roots = mRoots;
1332 roots.clear();
1333
1334 // Finds all the roots in the graph: all nodes with no dependencies
1335 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001336 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001337 if (node.dependencies.size() == 0) roots.add(node);
1338 }
1339
1340 return roots;
1341 }
1342
1343 /**
1344 * Prints the dependency graph for the specified rules.
1345 *
1346 * @param resources The context's resources to print the ids.
1347 * @param rules The list of rules to take into account.
1348 */
1349 void log(Resources resources, int... rules) {
Romain Guy725015a2009-06-23 14:27:34 -07001350 final LinkedList<Node> roots = findRoots(rules);
1351 for (Node node : roots) {
1352 printNode(resources, node);
1353 }
1354 }
1355
Romain Guy9fffa1e2009-06-24 12:42:43 -07001356 static void printViewId(Resources resources, View view) {
Romain Guy725015a2009-06-23 14:27:34 -07001357 if (view.getId() != View.NO_ID) {
1358 d(LOG_TAG, resources.getResourceEntryName(view.getId()));
1359 } else {
1360 d(LOG_TAG, "NO_ID");
1361 }
1362 }
1363
1364 private static void appendViewId(Resources resources, Node node, StringBuilder buffer) {
1365 if (node.view.getId() != View.NO_ID) {
1366 buffer.append(resources.getResourceEntryName(node.view.getId()));
1367 } else {
1368 buffer.append("NO_ID");
1369 }
1370 }
1371
1372 private static void printNode(Resources resources, Node node) {
1373 if (node.dependents.size() == 0) {
1374 printViewId(resources, node.view);
1375 } else {
1376 for (Node dependent : node.dependents) {
1377 StringBuilder buffer = new StringBuilder();
1378 appendViewId(resources, node, buffer);
1379 printdependents(resources, dependent, buffer);
1380 }
1381 }
1382 }
1383
1384 private static void printdependents(Resources resources, Node node, StringBuilder buffer) {
1385 buffer.append(" -> ");
1386 appendViewId(resources, node, buffer);
1387
1388 if (node.dependents.size() == 0) {
1389 d(LOG_TAG, buffer.toString());
1390 } else {
1391 for (Node dependent : node.dependents) {
1392 StringBuilder subBuffer = new StringBuilder(buffer);
1393 printdependents(resources, dependent, subBuffer);
1394 }
1395 }
1396 }
1397
1398 /**
1399 * A node in the dependency graph. A node is a view, its list of dependencies
1400 * and its list of dependents.
1401 *
1402 * A node with no dependent is considered a root of the graph.
1403 */
1404 static class Node implements Poolable<Node> {
1405 /**
1406 * The view representing this node in the layout.
1407 */
1408 View view;
1409
1410 /**
1411 * The list of dependents for this node; a dependent is a node
1412 * that needs this node to be processed first.
1413 */
1414 final HashSet<Node> dependents = new HashSet<Node>();
1415
1416 /**
1417 * The list of dependencies for this node.
1418 */
1419 final SparseArray<Node> dependencies = new SparseArray<Node>();
1420
1421 /*
1422 * START POOL IMPLEMENTATION
1423 */
Romain Guybaac4632009-06-29 14:28:29 -07001424 // The pool is static, so all nodes instances are shared across
1425 // activities, that's why we give it a rather high limit
1426 private static final int POOL_LIMIT = 100;
Romain Guy725015a2009-06-23 14:27:34 -07001427 private static final Pool<Node> sPool = Pools.synchronizedPool(
1428 Pools.finitePool(new PoolableManager<Node>() {
1429 public Node newInstance() {
1430 return new Node();
1431 }
1432
1433 public void onAcquired(Node element) {
1434 }
1435
1436 public void onReleased(Node element) {
1437 }
1438 }, POOL_LIMIT)
1439 );
1440
1441 private Node mNext;
1442
1443 public void setNextPoolable(Node element) {
1444 mNext = element;
1445 }
1446
1447 public Node getNextPoolable() {
1448 return mNext;
1449 }
1450
1451 static Node acquire(View view) {
1452 final Node node = sPool.acquire();
1453 node.view = view;
1454
1455 return node;
1456 }
1457
1458 void release() {
1459 view = null;
1460 dependents.clear();
1461 dependencies.clear();
1462
1463 sPool.release(this);
1464 }
1465 /*
1466 * END POOL IMPLEMENTATION
1467 */
1468 }
1469 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470}