blob: e52e84df3fa28e8d394f3a2997b68ddc7c9f578d [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
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070019import com.android.internal.R;
20
Romain Guybc5d8762012-01-06 16:40:49 -080021import java.util.ArrayDeque;
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070022import java.util.ArrayList;
23import java.util.Comparator;
Romain Guybc5d8762012-01-06 16:40:49 -080024import java.util.HashMap;
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070025import java.util.SortedSet;
26import java.util.TreeSet;
27
svetoslavganov75986cf2009-05-14 22:28:01 -070028import android.content.Context;
Romain Guy725015a2009-06-23 14:27:34 -070029import android.content.res.Resources;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070030import android.content.res.TypedArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070031import android.graphics.Rect;
32import android.util.AttributeSet;
Romain Guy725015a2009-06-23 14:27:34 -070033import android.util.Pool;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070034import android.util.Poolable;
Romain Guy725015a2009-06-23 14:27:34 -070035import android.util.PoolableManager;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070036import android.util.Pools;
37import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070038import android.view.Gravity;
39import android.view.View;
40import android.view.ViewDebug;
41import android.view.ViewGroup;
42import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080043import android.view.accessibility.AccessibilityNodeInfo;
svetoslavganov75986cf2009-05-14 22:28:01 -070044import android.widget.RemoteViews.RemoteView;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070045
46import static android.util.Log.d;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48/**
49 * 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 -080050 * parent.
Romain Guya1f3e4a2009-06-04 15:10:46 -070051 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 * <p>
53 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
54 * position of its children. For example, you cannot have a RelativeLayout whose height is set to
55 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
56 * {@link #ALIGN_PARENT_BOTTOM}.
57 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070058 *
Scott Main4c359b72012-07-24 15:51:27 -070059 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
60 * Layout</a> guide.</p>
Scott Main41ec6532010-08-19 16:57:07 -070061 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 * <p>
63 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
64 * layout attributes
65 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070066 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 * @attr ref android.R.styleable#RelativeLayout_gravity
68 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
69 */
70@RemoteView
71public class RelativeLayout extends ViewGroup {
Romain Guy725015a2009-06-23 14:27:34 -070072 private static final String LOG_TAG = "RelativeLayout";
73
74 private static final boolean DEBUG_GRAPH = false;
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 public static final int TRUE = -1;
77
78 /**
79 * Rule that aligns a child's right edge with another child's left edge.
80 */
81 public static final int LEFT_OF = 0;
82 /**
83 * Rule that aligns a child's left edge with another child's right edge.
84 */
85 public static final int RIGHT_OF = 1;
86 /**
87 * Rule that aligns a child's bottom edge with another child's top edge.
88 */
89 public static final int ABOVE = 2;
90 /**
91 * Rule that aligns a child's top edge with another child's bottom edge.
92 */
93 public static final int BELOW = 3;
94
95 /**
96 * Rule that aligns a child's baseline with another child's baseline.
97 */
98 public static final int ALIGN_BASELINE = 4;
99 /**
100 * Rule that aligns a child's left edge with another child's left edge.
101 */
102 public static final int ALIGN_LEFT = 5;
103 /**
104 * Rule that aligns a child's top edge with another child's top edge.
105 */
106 public static final int ALIGN_TOP = 6;
107 /**
108 * Rule that aligns a child's right edge with another child's right edge.
109 */
110 public static final int ALIGN_RIGHT = 7;
111 /**
112 * Rule that aligns a child's bottom edge with another child's bottom edge.
113 */
114 public static final int ALIGN_BOTTOM = 8;
115
116 /**
117 * Rule that aligns the child's left edge with its RelativeLayout
118 * parent's left edge.
119 */
120 public static final int ALIGN_PARENT_LEFT = 9;
121 /**
122 * Rule that aligns the child's top edge with its RelativeLayout
123 * parent's top edge.
124 */
125 public static final int ALIGN_PARENT_TOP = 10;
126 /**
127 * Rule that aligns the child's right edge with its RelativeLayout
128 * parent's right edge.
129 */
130 public static final int ALIGN_PARENT_RIGHT = 11;
131 /**
132 * Rule that aligns the child's bottom edge with its RelativeLayout
133 * parent's bottom edge.
134 */
135 public static final int ALIGN_PARENT_BOTTOM = 12;
136
137 /**
138 * Rule that centers the child with respect to the bounds of its
139 * RelativeLayout parent.
140 */
141 public static final int CENTER_IN_PARENT = 13;
142 /**
143 * Rule that centers the child horizontally with respect to the
144 * bounds of its RelativeLayout parent.
145 */
146 public static final int CENTER_HORIZONTAL = 14;
147 /**
148 * Rule that centers the child vertically with respect to the
149 * bounds of its RelativeLayout parent.
150 */
151 public static final int CENTER_VERTICAL = 15;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700152 /**
153 * Rule that aligns a child's end edge with another child's start edge.
154 */
155 public static final int START_OF = 16;
156 /**
157 * Rule that aligns a child's start edge with another child's end edge.
158 */
159 public static final int END_OF = 17;
160 /**
161 * Rule that aligns a child's start edge with another child's start edge.
162 */
163 public static final int ALIGN_START = 18;
164 /**
165 * Rule that aligns a child's end edge with another child's end edge.
166 */
167 public static final int ALIGN_END = 19;
168 /**
169 * Rule that aligns the child's start edge with its RelativeLayout
170 * parent's start edge.
171 */
172 public static final int ALIGN_PARENT_START = 20;
173 /**
174 * Rule that aligns the child's end edge with its RelativeLayout
175 * parent's end edge.
176 */
177 public static final int ALIGN_PARENT_END = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700179 private static final int VERB_COUNT = 22;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180
Romain Guybc5d8762012-01-06 16:40:49 -0800181
182 private static final int[] RULES_VERTICAL = {
183 ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
184 };
185
186 private static final int[] RULES_HORIZONTAL = {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700187 LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
Romain Guybc5d8762012-01-06 16:40:49 -0800188 };
189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 private View mBaselineView = null;
191 private boolean mHasBaselineAlignedChild;
192
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700193 private int mGravity = Gravity.START | Gravity.TOP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 private final Rect mContentBounds = new Rect();
195 private final Rect mSelfBounds = new Rect();
196 private int mIgnoreGravity;
197
Romain Guy725015a2009-06-23 14:27:34 -0700198 private SortedSet<View> mTopToBottomLeftToRightSet = null;
199
200 private boolean mDirtyHierarchy;
201 private View[] mSortedHorizontalChildren = new View[0];
202 private View[] mSortedVerticalChildren = new View[0];
203 private final DependencyGraph mGraph = new DependencyGraph();
svetoslavganov75986cf2009-05-14 22:28:01 -0700204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 public RelativeLayout(Context context) {
206 super(context);
207 }
208
209 public RelativeLayout(Context context, AttributeSet attrs) {
210 super(context, attrs);
211 initFromAttributes(context, attrs);
212 }
213
214 public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
215 super(context, attrs, defStyle);
216 initFromAttributes(context, attrs);
217 }
218
219 private void initFromAttributes(Context context, AttributeSet attrs) {
220 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout);
221 mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
222 mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
223 a.recycle();
224 }
225
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700226 @Override
227 public boolean shouldDelayChildPressedState() {
228 return false;
229 }
230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 /**
232 * Defines which View is ignored when the gravity is applied. This setting has no
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700233 * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 *
235 * @param viewId The id of the View to be ignored by gravity, or 0 if no View
236 * should be ignored.
237 *
238 * @see #setGravity(int)
239 *
240 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
241 */
242 @android.view.RemotableViewMethod
243 public void setIgnoreGravity(int viewId) {
244 mIgnoreGravity = viewId;
245 }
246
247 /**
Philip Milne1018fb42012-03-13 12:00:04 -0700248 * Describes how the child views are positioned.
249 *
250 * @return the gravity.
251 *
252 * @see #setGravity(int)
253 * @see android.view.Gravity
254 *
255 * @attr ref android.R.styleable#RelativeLayout_gravity
256 */
257 public int getGravity() {
258 return mGravity;
259 }
260
261 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 * Describes how the child views are positioned. Defaults to
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700263 * <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 *
Adam Powell1fec24e2011-09-15 14:19:30 -0700265 * <p>Note that since RelativeLayout considers the positioning of each child
266 * relative to one another to be significant, setting gravity will affect
267 * the positioning of all children as a single unit within the parent.
268 * This happens after children have been relatively positioned.</p>
269 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 * @param gravity See {@link android.view.Gravity}
271 *
272 * @see #setHorizontalGravity(int)
273 * @see #setVerticalGravity(int)
274 *
275 * @attr ref android.R.styleable#RelativeLayout_gravity
276 */
277 @android.view.RemotableViewMethod
278 public void setGravity(int gravity) {
279 if (mGravity != gravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700280 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700281 gravity |= Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 }
283
284 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
285 gravity |= Gravity.TOP;
286 }
287
288 mGravity = gravity;
289 requestLayout();
290 }
291 }
292
293 @android.view.RemotableViewMethod
294 public void setHorizontalGravity(int horizontalGravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700295 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
296 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
297 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 requestLayout();
299 }
300 }
301
302 @android.view.RemotableViewMethod
303 public void setVerticalGravity(int verticalGravity) {
304 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
305 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
306 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
307 requestLayout();
308 }
309 }
310
311 @Override
312 public int getBaseline() {
313 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
314 }
315
316 @Override
Romain Guy725015a2009-06-23 14:27:34 -0700317 public void requestLayout() {
318 super.requestLayout();
319 mDirtyHierarchy = true;
320 }
321
322 private void sortChildren() {
323 int count = getChildCount();
324 if (mSortedVerticalChildren.length != count) mSortedVerticalChildren = new View[count];
325 if (mSortedHorizontalChildren.length != count) mSortedHorizontalChildren = new View[count];
326
327 final DependencyGraph graph = mGraph;
328 graph.clear();
329
330 for (int i = 0; i < count; i++) {
331 final View child = getChildAt(i);
332 graph.add(child);
333 }
334
335 if (DEBUG_GRAPH) {
336 d(LOG_TAG, "=== Sorted vertical children");
Romain Guybc5d8762012-01-06 16:40:49 -0800337 graph.log(getResources(), RULES_VERTICAL);
Romain Guy725015a2009-06-23 14:27:34 -0700338 d(LOG_TAG, "=== Sorted horizontal children");
Romain Guybc5d8762012-01-06 16:40:49 -0800339 graph.log(getResources(), RULES_HORIZONTAL);
Romain Guy725015a2009-06-23 14:27:34 -0700340 }
341
Romain Guybc5d8762012-01-06 16:40:49 -0800342 graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
343 graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
Romain Guy9fffa1e2009-06-24 12:42:43 -0700344
345 if (DEBUG_GRAPH) {
346 d(LOG_TAG, "=== Ordered list of vertical children");
347 for (View view : mSortedVerticalChildren) {
348 DependencyGraph.printViewId(getResources(), view);
349 }
350 d(LOG_TAG, "=== Ordered list of horizontal children");
351 for (View view : mSortedHorizontalChildren) {
352 DependencyGraph.printViewId(getResources(), view);
353 }
354 }
Romain Guy725015a2009-06-23 14:27:34 -0700355 }
356
Romain Guy42460ac2010-01-11 16:46:33 -0800357 // TODO: we need to find another way to implement RelativeLayout
358 // This implementation cannot handle every case
Romain Guy725015a2009-06-23 14:27:34 -0700359 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Romain Guy725015a2009-06-23 14:27:34 -0700361 if (mDirtyHierarchy) {
362 mDirtyHierarchy = false;
363 sortChildren();
364 }
365
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 int myWidth = -1;
367 int myHeight = -1;
368
369 int width = 0;
370 int height = 0;
371
Adam Powell946d05b2012-10-01 15:06:20 -0700372 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
373 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
374 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
375 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376
377 // Record our dimensions if they are known;
378 if (widthMode != MeasureSpec.UNSPECIFIED) {
379 myWidth = widthSize;
380 }
381
382 if (heightMode != MeasureSpec.UNSPECIFIED) {
383 myHeight = heightSize;
384 }
385
386 if (widthMode == MeasureSpec.EXACTLY) {
387 width = myWidth;
388 }
389
390 if (heightMode == MeasureSpec.EXACTLY) {
391 height = myHeight;
392 }
393
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 mHasBaselineAlignedChild = false;
395
396 View ignore = null;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700397 int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700398 final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
400 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
401
402 int left = Integer.MAX_VALUE;
403 int top = Integer.MAX_VALUE;
404 int right = Integer.MIN_VALUE;
405 int bottom = Integer.MIN_VALUE;
406
Romain Guyf7dabb02009-06-25 14:47:14 -0700407 boolean offsetHorizontalAxis = false;
408 boolean offsetVerticalAxis = false;
409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
411 ignore = findViewById(mIgnoreGravity);
412 }
413
Romain Guyf7dabb02009-06-25 14:47:14 -0700414 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
415 final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
416
Romain Guye24ef602009-06-25 13:01:55 -0700417 View[] views = mSortedHorizontalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700418 int count = views.length;
419 for (int i = 0; i < count; i++) {
420 View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 if (child.getVisibility() != GONE) {
422 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guy95607032009-06-24 14:37:03 -0700423
424 applyHorizontalSizeRules(params, myWidth);
Romain Guyf782e602009-06-25 15:26:49 -0700425 measureChildHorizontal(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700426 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
427 offsetHorizontalAxis = true;
428 }
Romain Guy725015a2009-06-23 14:27:34 -0700429 }
430 }
431
Romain Guye24ef602009-06-25 13:01:55 -0700432 views = mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700433 count = views.length;
Romain Guyf7dabb02009-06-25 14:47:14 -0700434
Romain Guy725015a2009-06-23 14:27:34 -0700435 for (int i = 0; i < count; i++) {
436 View child = views[i];
437 if (child.getVisibility() != GONE) {
438 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guy95607032009-06-24 14:37:03 -0700439
440 applyVerticalSizeRules(params, myHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 measureChild(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700442 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
443 offsetVerticalAxis = true;
444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445
Romain Guyf7dabb02009-06-25 14:47:14 -0700446 if (isWrapContentWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 width = Math.max(width, params.mRight);
448 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700449
450 if (isWrapContentHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 height = Math.max(height, params.mBottom);
452 }
453
454 if (child != ignore || verticalGravity) {
455 left = Math.min(left, params.mLeft - params.leftMargin);
456 top = Math.min(top, params.mTop - params.topMargin);
457 }
458
459 if (child != ignore || horizontalGravity) {
460 right = Math.max(right, params.mRight + params.rightMargin);
461 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
462 }
463 }
464 }
465
466 if (mHasBaselineAlignedChild) {
Romain Guy725015a2009-06-23 14:27:34 -0700467 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 View child = getChildAt(i);
469 if (child.getVisibility() != GONE) {
470 LayoutParams params = (LayoutParams) child.getLayoutParams();
471 alignBaseline(child, params);
472
473 if (child != ignore || verticalGravity) {
Romain Guy725015a2009-06-23 14:27:34 -0700474 left = Math.min(left, params.mLeft - params.leftMargin);
475 top = Math.min(top, params.mTop - params.topMargin);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 }
477
478 if (child != ignore || horizontalGravity) {
479 right = Math.max(right, params.mRight + params.rightMargin);
480 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
481 }
482 }
483 }
484 }
485
Fabrice Di Megliobb4b6012012-10-26 16:27:55 -0700486 final int layoutDirection = getLayoutDirection();
487
Romain Guyf7dabb02009-06-25 14:47:14 -0700488 if (isWrapContentWidth) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700489 // Width already has left padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 // the right of each child view
491 width += mPaddingRight;
492
493 if (mLayoutParams.width >= 0) {
494 width = Math.max(width, mLayoutParams.width);
495 }
496
497 width = Math.max(width, getSuggestedMinimumWidth());
498 width = resolveSize(width, widthMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700499
500 if (offsetHorizontalAxis) {
Romain Guyd10a5762009-07-28 11:18:14 -0700501 for (int i = 0; i < count; i++) {
Romain Guyf7dabb02009-06-25 14:47:14 -0700502 View child = getChildAt(i);
503 if (child.getVisibility() != GONE) {
504 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700505 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700506 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
507 centerHorizontal(child, params, width);
Romain Guy42460ac2010-01-11 16:46:33 -0800508 } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
509 final int childWidth = child.getMeasuredWidth();
510 params.mLeft = width - mPaddingRight - childWidth;
511 params.mRight = params.mLeft + childWidth;
Romain Guyf7dabb02009-06-25 14:47:14 -0700512 }
513 }
514 }
515 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700517
518 if (isWrapContentHeight) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700519 // Height already has top padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 // the bottom of each child view
521 height += mPaddingBottom;
522
523 if (mLayoutParams.height >= 0) {
524 height = Math.max(height, mLayoutParams.height);
525 }
526
527 height = Math.max(height, getSuggestedMinimumHeight());
528 height = resolveSize(height, heightMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700529
530 if (offsetVerticalAxis) {
531 for (int i = 0; i < count; i++) {
532 View child = getChildAt(i);
533 if (child.getVisibility() != GONE) {
534 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700535 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700536 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
537 centerVertical(child, params, height);
Romain Guy42460ac2010-01-11 16:46:33 -0800538 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
539 final int childHeight = child.getMeasuredHeight();
540 params.mTop = height - mPaddingBottom - childHeight;
541 params.mBottom = params.mTop + childHeight;
Romain Guyf7dabb02009-06-25 14:47:14 -0700542 }
543 }
544 }
545 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 }
547
548 if (horizontalGravity || verticalGravity) {
549 final Rect selfBounds = mSelfBounds;
550 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
551 height - mPaddingBottom);
552
553 final Rect contentBounds = mContentBounds;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700554 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
Fabrice Di Meglioc0053222011-06-13 12:16:51 -0700555 layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556
557 final int horizontalOffset = contentBounds.left - left;
558 final int verticalOffset = contentBounds.top - top;
559 if (horizontalOffset != 0 || verticalOffset != 0) {
Romain Guy725015a2009-06-23 14:27:34 -0700560 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 View child = getChildAt(i);
562 if (child.getVisibility() != GONE && child != ignore) {
563 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guyd10a5762009-07-28 11:18:14 -0700564 if (horizontalGravity) {
565 params.mLeft += horizontalOffset;
566 params.mRight += horizontalOffset;
567 }
568 if (verticalGravity) {
569 params.mTop += verticalOffset;
570 params.mBottom += verticalOffset;
571 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 }
573 }
574 }
575 }
576
577 setMeasuredDimension(width, height);
578 }
579
580 private void alignBaseline(View child, LayoutParams params) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700581 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700582 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE);
584
585 if (anchorBaseline != -1) {
586 LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE);
587 if (anchorParams != null) {
588 int offset = anchorParams.mTop + anchorBaseline;
589 int baseline = child.getBaseline();
590 if (baseline != -1) {
591 offset -= baseline;
592 }
593 int height = params.mBottom - params.mTop;
594 params.mTop = offset;
595 params.mBottom = params.mTop + height;
596 }
597 }
598
599 if (mBaselineView == null) {
600 mBaselineView = child;
601 } else {
602 LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams();
603 if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) {
604 mBaselineView = child;
605 }
606 }
607 }
608
609 /**
610 * Measure a child. The child should have left, top, right and bottom information
611 * stored in its LayoutParams. If any of these values is -1 it means that the view
612 * can extend up to the corresponding edge.
613 *
614 * @param child Child to measure
615 * @param params LayoutParams associated with child
616 * @param myWidth Width of the the RelativeLayout
617 * @param myHeight Height of the RelativeLayout
618 */
Romain Guy725015a2009-06-23 14:27:34 -0700619 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
621 params.mRight, params.width,
622 params.leftMargin, params.rightMargin,
623 mPaddingLeft, mPaddingRight,
624 myWidth);
625 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
626 params.mBottom, params.height,
627 params.topMargin, params.bottomMargin,
628 mPaddingTop, mPaddingBottom,
629 myHeight);
630 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
631 }
632
Romain Guyf782e602009-06-25 15:26:49 -0700633 private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
Romain Guy95607032009-06-24 14:37:03 -0700634 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
635 params.mRight, params.width,
636 params.leftMargin, params.rightMargin,
637 mPaddingLeft, mPaddingRight,
638 myWidth);
Romain Guyf782e602009-06-25 15:26:49 -0700639 int childHeightMeasureSpec;
Adam Powell946d05b2012-10-01 15:06:20 -0700640 if (params.width == LayoutParams.MATCH_PARENT) {
Romain Guybaac4632009-06-29 14:28:29 -0700641 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
Romain Guyf782e602009-06-25 15:26:49 -0700642 } else {
Romain Guybaac4632009-06-29 14:28:29 -0700643 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
Romain Guyf782e602009-06-25 15:26:49 -0700644 }
Romain Guy725015a2009-06-23 14:27:34 -0700645 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
646 }
647
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 /**
649 * Get a measure spec that accounts for all of the constraints on this view.
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700650 * This includes size constraints imposed by the RelativeLayout as well as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 * the View's desired dimension.
652 *
653 * @param childStart The left or top field of the child's layout params
654 * @param childEnd The right or bottom field of the child's layout params
655 * @param childSize The child's desired size (the width or height field of
656 * the child's layout params)
657 * @param startMargin The left or top margin
658 * @param endMargin The right or bottom margin
659 * @param startPadding mPaddingLeft or mPaddingTop
660 * @param endPadding mPaddingRight or mPaddingBottom
661 * @param mySize The width or height of this view (the RelativeLayout)
662 * @return MeasureSpec for the child
663 */
664 private int getChildMeasureSpec(int childStart, int childEnd,
665 int childSize, int startMargin, int endMargin, int startPadding,
666 int endPadding, int mySize) {
667 int childSpecMode = 0;
668 int childSpecSize = 0;
669
670 // Figure out start and end bounds.
671 int tempStart = childStart;
672 int tempEnd = childEnd;
673
674 // If the view did not express a layout constraint for an edge, use
675 // view's margins and our padding
676 if (tempStart < 0) {
677 tempStart = startPadding + startMargin;
678 }
679 if (tempEnd < 0) {
680 tempEnd = mySize - endPadding - endMargin;
681 }
682
683 // Figure out maximum size available to this view
684 int maxAvailable = tempEnd - tempStart;
685
686 if (childStart >= 0 && childEnd >= 0) {
687 // Constraints fixed both edges, so child must be an exact size
688 childSpecMode = MeasureSpec.EXACTLY;
689 childSpecSize = maxAvailable;
690 } else {
691 if (childSize >= 0) {
692 // Child wanted an exact size. Give as much as possible
693 childSpecMode = MeasureSpec.EXACTLY;
694
695 if (maxAvailable >= 0) {
696 // We have a maxmum size in this dimension.
697 childSpecSize = Math.min(maxAvailable, childSize);
698 } else {
699 // We can grow in this dimension.
700 childSpecSize = childSize;
701 }
Romain Guy980a9382010-01-08 15:06:28 -0800702 } else if (childSize == LayoutParams.MATCH_PARENT) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700703 // Child wanted to be as big as possible. Give all available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 // space
705 childSpecMode = MeasureSpec.EXACTLY;
706 childSpecSize = maxAvailable;
707 } else if (childSize == LayoutParams.WRAP_CONTENT) {
708 // Child wants to wrap content. Use AT_MOST
709 // to communicate available space if we know
710 // our max size
711 if (maxAvailable >= 0) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700712 // We have a maximum size in this dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 childSpecMode = MeasureSpec.AT_MOST;
714 childSpecSize = maxAvailable;
715 } else {
716 // We can grow in this dimension. Child can be as big as it
717 // wants
718 childSpecMode = MeasureSpec.UNSPECIFIED;
719 childSpecSize = 0;
720 }
721 }
722 }
723
724 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
725 }
726
Romain Guyf7dabb02009-06-25 14:47:14 -0700727 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
728 boolean wrapContent) {
729
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700730 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700731 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732
733 if (params.mLeft < 0 && params.mRight >= 0) {
734 // Right is fixed, but left varies
735 params.mLeft = params.mRight - child.getMeasuredWidth();
736 } else if (params.mLeft >= 0 && params.mRight < 0) {
737 // Left is fixed, but right varies
738 params.mRight = params.mLeft + child.getMeasuredWidth();
739 } else if (params.mLeft < 0 && params.mRight < 0) {
740 // Both left and right vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700741 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
742 if (!wrapContent) {
743 centerHorizontal(child, params, myWidth);
744 } else {
745 params.mLeft = mPaddingLeft + params.leftMargin;
746 params.mRight = params.mLeft + child.getMeasuredWidth();
747 }
748 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 } else {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700750 // This is the default case. For RTL we start from the right and for LTR we start
751 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
752 if (isLayoutRtl()) {
753 params.mRight = myWidth - mPaddingRight- params.rightMargin;
754 params.mLeft = params.mRight - child.getMeasuredWidth();
755 } else {
756 params.mLeft = mPaddingLeft + params.leftMargin;
757 params.mRight = params.mLeft + child.getMeasuredWidth();
758 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 }
760 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700761 return rules[ALIGN_PARENT_END] != 0;
Romain Guy725015a2009-06-23 14:27:34 -0700762 }
763
Romain Guyf7dabb02009-06-25 14:47:14 -0700764 private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
765 boolean wrapContent) {
766
Romain Guy725015a2009-06-23 14:27:34 -0700767 int[] rules = params.getRules();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768
769 if (params.mTop < 0 && params.mBottom >= 0) {
770 // Bottom is fixed, but top varies
771 params.mTop = params.mBottom - child.getMeasuredHeight();
772 } else if (params.mTop >= 0 && params.mBottom < 0) {
773 // Top is fixed, but bottom varies
774 params.mBottom = params.mTop + child.getMeasuredHeight();
775 } else if (params.mTop < 0 && params.mBottom < 0) {
776 // Both top and bottom vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700777 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
778 if (!wrapContent) {
779 centerVertical(child, params, myHeight);
780 } else {
781 params.mTop = mPaddingTop + params.topMargin;
782 params.mBottom = params.mTop + child.getMeasuredHeight();
783 }
784 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 } else {
786 params.mTop = mPaddingTop + params.topMargin;
787 params.mBottom = params.mTop + child.getMeasuredHeight();
788 }
789 }
Romain Guy42460ac2010-01-11 16:46:33 -0800790 return rules[ALIGN_PARENT_BOTTOM] != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 }
792
Romain Guy725015a2009-06-23 14:27:34 -0700793 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700794 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700795 int[] rules = childParams.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 RelativeLayout.LayoutParams anchorParams;
797
798 // -1 indicated a "soft requirement" in that direction. For example:
799 // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right
800 // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left
801 // left=10, right=20 means the left and right ends are both fixed
802 childParams.mLeft = -1;
803 childParams.mRight = -1;
804
805 anchorParams = getRelatedViewParams(rules, LEFT_OF);
806 if (anchorParams != null) {
807 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
808 childParams.rightMargin);
809 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
810 if (myWidth >= 0) {
811 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
812 } else {
813 // FIXME uh oh...
814 }
815 }
816
817 anchorParams = getRelatedViewParams(rules, RIGHT_OF);
818 if (anchorParams != null) {
819 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
820 childParams.leftMargin);
821 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
822 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
823 }
824
825 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
826 if (anchorParams != null) {
827 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
828 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
829 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
830 }
831
832 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
833 if (anchorParams != null) {
834 childParams.mRight = anchorParams.mRight - childParams.rightMargin;
835 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
836 if (myWidth >= 0) {
837 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
838 } else {
839 // FIXME uh oh...
840 }
841 }
842
843 if (0 != rules[ALIGN_PARENT_LEFT]) {
844 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
845 }
846
847 if (0 != rules[ALIGN_PARENT_RIGHT]) {
848 if (myWidth >= 0) {
849 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
850 } else {
851 // FIXME uh oh...
852 }
853 }
Romain Guy725015a2009-06-23 14:27:34 -0700854 }
855
856 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) {
857 int[] rules = childParams.getRules();
858 RelativeLayout.LayoutParams anchorParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859
860 childParams.mTop = -1;
861 childParams.mBottom = -1;
862
863 anchorParams = getRelatedViewParams(rules, ABOVE);
864 if (anchorParams != null) {
865 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
866 childParams.bottomMargin);
867 } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
868 if (myHeight >= 0) {
869 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
870 } else {
871 // FIXME uh oh...
872 }
873 }
874
875 anchorParams = getRelatedViewParams(rules, BELOW);
876 if (anchorParams != null) {
877 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
878 childParams.topMargin);
879 } else if (childParams.alignWithParent && rules[BELOW] != 0) {
880 childParams.mTop = mPaddingTop + childParams.topMargin;
881 }
882
883 anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
884 if (anchorParams != null) {
885 childParams.mTop = anchorParams.mTop + childParams.topMargin;
886 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
887 childParams.mTop = mPaddingTop + childParams.topMargin;
888 }
889
890 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
891 if (anchorParams != null) {
892 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
893 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
894 if (myHeight >= 0) {
895 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
896 } else {
897 // FIXME uh oh...
898 }
899 }
900
901 if (0 != rules[ALIGN_PARENT_TOP]) {
902 childParams.mTop = mPaddingTop + childParams.topMargin;
903 }
904
905 if (0 != rules[ALIGN_PARENT_BOTTOM]) {
906 if (myHeight >= 0) {
907 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
908 } else {
909 // FIXME uh oh...
910 }
911 }
912
913 if (rules[ALIGN_BASELINE] != 0) {
914 mHasBaselineAlignedChild = true;
915 }
916 }
917
918 private View getRelatedView(int[] rules, int relation) {
919 int id = rules[relation];
920 if (id != 0) {
Romain Guy1ab621e2009-06-25 13:31:57 -0700921 DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
Romain Guya0fd1d72009-06-24 14:25:43 -0700922 if (node == null) return null;
923 View v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924
925 // Find the first non-GONE view up the chain
926 while (v.getVisibility() == View.GONE) {
927 rules = ((LayoutParams) v.getLayoutParams()).getRules();
Romain Guy1ab621e2009-06-25 13:31:57 -0700928 node = mGraph.mKeyNodes.get((rules[relation]));
Romain Guya0fd1d72009-06-24 14:25:43 -0700929 if (node == null) return null;
930 v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 }
932
933 return v;
934 }
935
936 return null;
937 }
938
939 private LayoutParams getRelatedViewParams(int[] rules, int relation) {
940 View v = getRelatedView(rules, relation);
941 if (v != null) {
942 ViewGroup.LayoutParams params = v.getLayoutParams();
943 if (params instanceof LayoutParams) {
944 return (LayoutParams) v.getLayoutParams();
945 }
946 }
947 return null;
948 }
949
950 private int getRelatedViewBaseline(int[] rules, int relation) {
951 View v = getRelatedView(rules, relation);
952 if (v != null) {
953 return v.getBaseline();
954 }
955 return -1;
956 }
957
958 private void centerHorizontal(View child, LayoutParams params, int myWidth) {
959 int childWidth = child.getMeasuredWidth();
960 int left = (myWidth - childWidth) / 2;
961
962 params.mLeft = left;
963 params.mRight = left + childWidth;
964 }
965
966 private void centerVertical(View child, LayoutParams params, int myHeight) {
967 int childHeight = child.getMeasuredHeight();
968 int top = (myHeight - childHeight) / 2;
969
970 params.mTop = top;
971 params.mBottom = top + childHeight;
972 }
973
974 @Override
975 protected void onLayout(boolean changed, int l, int t, int r, int b) {
976 // The layout has actually already been performed and the positions
977 // cached. Apply the cached values to the children.
978 int count = getChildCount();
979
980 for (int i = 0; i < count; i++) {
981 View child = getChildAt(i);
982 if (child.getVisibility() != GONE) {
983 RelativeLayout.LayoutParams st =
984 (RelativeLayout.LayoutParams) child.getLayoutParams();
985 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 }
987 }
988 }
989
990 @Override
991 public LayoutParams generateLayoutParams(AttributeSet attrs) {
992 return new RelativeLayout.LayoutParams(getContext(), attrs);
993 }
994
995 /**
996 * Returns a set of layout parameters with a width of
997 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
998 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
999 */
1000 @Override
1001 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1002 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1003 }
1004
1005 // Override to allow type-checking of LayoutParams.
1006 @Override
1007 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1008 return p instanceof RelativeLayout.LayoutParams;
1009 }
1010
1011 @Override
1012 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1013 return new LayoutParams(p);
1014 }
1015
svetoslavganov75986cf2009-05-14 22:28:01 -07001016 @Override
1017 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1018 if (mTopToBottomLeftToRightSet == null) {
1019 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1020 }
1021
1022 // sort children top-to-bottom and left-to-right
1023 for (int i = 0, count = getChildCount(); i < count; i++) {
1024 mTopToBottomLeftToRightSet.add(getChildAt(i));
1025 }
1026
1027 for (View view : mTopToBottomLeftToRightSet) {
Svetoslav Ganov0b0a41d2011-09-07 18:06:03 -07001028 if (view.getVisibility() == View.VISIBLE
1029 && view.dispatchPopulateAccessibilityEvent(event)) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001030 mTopToBottomLeftToRightSet.clear();
1031 return true;
1032 }
1033 }
1034
1035 mTopToBottomLeftToRightSet.clear();
1036 return false;
1037 }
1038
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001039 @Override
1040 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1041 super.onInitializeAccessibilityEvent(event);
1042 event.setClassName(RelativeLayout.class.getName());
1043 }
1044
1045 @Override
1046 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1047 super.onInitializeAccessibilityNodeInfo(info);
1048 info.setClassName(RelativeLayout.class.getName());
1049 }
1050
svetoslavganov75986cf2009-05-14 22:28:01 -07001051 /**
1052 * Compares two views in left-to-right and top-to-bottom fashion.
1053 */
1054 private class TopToBottomLeftToRightComparator implements Comparator<View> {
1055 public int compare(View first, View second) {
1056 // top - bottom
1057 int topDifference = first.getTop() - second.getTop();
1058 if (topDifference != 0) {
1059 return topDifference;
1060 }
1061 // left - right
1062 int leftDifference = first.getLeft() - second.getLeft();
1063 if (leftDifference != 0) {
1064 return leftDifference;
1065 }
1066 // break tie by height
1067 int heightDiference = first.getHeight() - second.getHeight();
1068 if (heightDiference != 0) {
1069 return heightDiference;
1070 }
1071 // break tie by width
1072 int widthDiference = first.getWidth() - second.getWidth();
1073 if (widthDiference != 0) {
1074 return widthDiference;
1075 }
1076 return 0;
1077 }
1078 }
1079
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 /**
1081 * Per-child layout information associated with RelativeLayout.
1082 *
1083 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1084 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1085 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1086 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1087 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1088 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1089 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1090 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1091 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1092 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1093 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1094 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1095 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1096 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1097 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1098 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1099 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001100 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1101 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1102 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1103 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1104 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1105 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 */
1107 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001108 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001109 @ViewDebug.IntToString(from = ABOVE, to = "above"),
1110 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
1111 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
1112 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"),
1113 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1114 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"),
1115 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"),
1116 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"),
1117 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"),
1118 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"),
1119 @ViewDebug.IntToString(from = BELOW, to = "below"),
1120 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"),
1121 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"),
1122 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"),
1123 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"),
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001124 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"),
1125 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"),
1126 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"),
1127 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"),
1128 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"),
1129 @ViewDebug.IntToString(from = START_OF, to = "startOf"),
1130 @ViewDebug.IntToString(from = END_OF, to = "endOf")
The Android Open Source Project10592532009-03-18 17:39:46 -07001131 }, mapping = {
1132 @ViewDebug.IntToString(from = TRUE, to = "true"),
Romain Guya1f3e4a2009-06-04 15:10:46 -07001133 @ViewDebug.IntToString(from = 0, to = "false/NO_ID")
The Android Open Source Project10592532009-03-18 17:39:46 -07001134 })
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 private int[] mRules = new int[VERB_COUNT];
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001137 private int[] mInitialRules = new int[VERB_COUNT];
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 private int mLeft, mTop, mRight, mBottom;
1140
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001141 private int mStart = DEFAULT_RELATIVE;
1142 private int mEnd = DEFAULT_RELATIVE;
1143
1144 private boolean mRulesChanged = false;
1145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 /**
1147 * When true, uses the parent as the anchor if the anchor doesn't exist or if
1148 * the anchor's visibility is GONE.
1149 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001150 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 public boolean alignWithParent;
1152
1153 public LayoutParams(Context c, AttributeSet attrs) {
1154 super(c, attrs);
1155
1156 TypedArray a = c.obtainStyledAttributes(attrs,
1157 com.android.internal.R.styleable.RelativeLayout_Layout);
1158
1159 final int[] rules = mRules;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001160 final int[] initialRules = mInitialRules;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161
1162 final int N = a.getIndexCount();
1163 for (int i = 0; i < N; i++) {
1164 int attr = a.getIndex(i);
1165 switch (attr) {
1166 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1167 alignWithParent = a.getBoolean(attr, false);
1168 break;
1169 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1170 rules[LEFT_OF] = a.getResourceId(attr, 0);
1171 break;
1172 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1173 rules[RIGHT_OF] = a.getResourceId(attr, 0);
1174 break;
1175 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1176 rules[ABOVE] = a.getResourceId(attr, 0);
1177 break;
1178 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1179 rules[BELOW] = a.getResourceId(attr, 0);
1180 break;
1181 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1182 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1183 break;
1184 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1185 rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1186 break;
1187 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1188 rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1189 break;
1190 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1191 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1192 break;
1193 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1194 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1195 break;
1196 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1197 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1198 break;
1199 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1200 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1201 break;
1202 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1203 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1204 break;
1205 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1206 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1207 break;
1208 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1209 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1210 break;
1211 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1212 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1213 break;
1214 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1215 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1216 break;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001217 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1218 rules[START_OF] = a.getResourceId(attr, 0);
1219 break;
1220 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1221 rules[END_OF] = a.getResourceId(attr, 0);
1222 break;
1223 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1224 rules[ALIGN_START] = a.getResourceId(attr, 0);
1225 break;
1226 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1227 rules[ALIGN_END] = a.getResourceId(attr, 0);
1228 break;
1229 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1230 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1231 break;
1232 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1233 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1234 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235 }
1236 }
1237
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001238 for (int n = LEFT_OF; n < VERB_COUNT; n++) {
1239 initialRules[n] = rules[n];
1240 }
1241
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 a.recycle();
1243 }
1244
1245 public LayoutParams(int w, int h) {
1246 super(w, h);
1247 }
1248
1249 /**
1250 * {@inheritDoc}
1251 */
1252 public LayoutParams(ViewGroup.LayoutParams source) {
1253 super(source);
1254 }
1255
1256 /**
1257 * {@inheritDoc}
1258 */
1259 public LayoutParams(ViewGroup.MarginLayoutParams source) {
1260 super(source);
1261 }
1262
1263 @Override
1264 public String debug(String output) {
1265 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1266 ", height=" + sizeToString(height) + " }";
1267 }
1268
1269 /**
1270 * Adds a layout rule to be interpreted by the RelativeLayout. This
1271 * method should only be used for constraints that don't refer to another sibling
1272 * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE}
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001273 * for true or 0 for false). To specify a verb that takes a subject, use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274 * {@link #addRule(int, int)} instead.
1275 *
1276 * @param verb One of the verbs defined by
1277 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1278 * ALIGN_WITH_PARENT_LEFT.
1279 * @see #addRule(int, int)
1280 */
1281 public void addRule(int verb) {
1282 mRules[verb] = TRUE;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001283 mInitialRules[verb] = TRUE;
1284 mRulesChanged = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 }
1286
1287 /**
1288 * Adds a layout rule to be interpreted by the RelativeLayout. Use this for
1289 * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
1290 * value (VISIBLE).
1291 *
1292 * @param verb One of the verbs defined by
1293 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1294 * ALIGN_WITH_PARENT_LEFT.
1295 * @param anchor The id of another view to use as an anchor,
1296 * or a boolean value(represented as {@link RelativeLayout#TRUE})
1297 * for true or 0 for false). For verbs that don't refer to another sibling
1298 * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
1299 * @see #addRule(int)
1300 */
1301 public void addRule(int verb, int anchor) {
1302 mRules[verb] = anchor;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001303 mInitialRules[verb] = anchor;
1304 mRulesChanged = true;
1305 }
1306
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001307 /**
1308 * Removes a layout rule to be interpreted by the RelativeLayout.
1309 *
1310 * @param verb One of the verbs defined by
1311 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1312 * ALIGN_WITH_PARENT_LEFT.
1313 * @see #addRule(int)
1314 * @see #addRule(int, int)
1315 */
1316 public void removeRule(int verb) {
1317 mRules[verb] = 0;
1318 mInitialRules[verb] = 0;
1319 mRulesChanged = true;
1320 }
1321
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001322 private boolean hasRelativeRules() {
1323 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1324 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1325 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1326 }
1327
1328 private void resolveRules(int layoutDirection) {
1329 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
1330 // Reset to initial state
1331 for (int n = LEFT_OF; n < VERB_COUNT; n++) {
1332 mRules[n] = mInitialRules[n];
1333 }
1334 // Apply rules depending on direction
1335 if (mRules[ALIGN_START] != 0) {
1336 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1337 }
1338 if (mRules[ALIGN_END] != 0) {
1339 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1340 }
1341 if (mRules[START_OF] != 0) {
1342 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1343 }
1344 if (mRules[END_OF] != 0) {
1345 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1346 }
1347 if (mRules[ALIGN_PARENT_START] != 0) {
1348 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1349 }
1350 if (mRules[ALIGN_PARENT_END] != 0) {
1351 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1352 }
1353 mRulesChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 }
1355
1356 /**
1357 * Retrieves a complete list of all supported rules, where the index is the rule
1358 * verb, and the element value is the value specified, or "false" if it was never
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001359 * set. If there are relative rules defined (*_START / *_END), they will be resolved
1360 * depending on the layout direction.
1361 *
1362 * @param layoutDirection the direction of the layout.
1363 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
1364 * or {@link View#LAYOUT_DIRECTION_RTL}
1365 * @return the supported rules
1366 * @see #addRule(int, int)
1367 *
1368 * @hide
1369 */
1370 public int[] getRules(int layoutDirection) {
1371 if (hasRelativeRules() &&
1372 (mRulesChanged || layoutDirection != getLayoutDirection())) {
1373 resolveRules(layoutDirection);
1374 if (layoutDirection != getLayoutDirection()) {
1375 setLayoutDirection(layoutDirection);
1376 }
1377 }
1378 return mRules;
1379 }
1380
1381 /**
1382 * Retrieves a complete list of all supported rules, where the index is the rule
1383 * verb, and the element value is the value specified, or "false" if it was never
1384 * set. There will be no resolution of relative rules done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 *
1386 * @return the supported rules
1387 * @see #addRule(int, int)
1388 */
1389 public int[] getRules() {
1390 return mRules;
1391 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001392
1393 @Override
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001394 public void resolveLayoutDirection(int layoutDirection) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001395 final boolean isLayoutRtl = isLayoutRtl();
1396 if (isLayoutRtl) {
1397 if (mStart != DEFAULT_RELATIVE) mRight = mStart;
1398 if (mEnd != DEFAULT_RELATIVE) mLeft = mEnd;
1399 } else {
1400 if (mStart != DEFAULT_RELATIVE) mLeft = mStart;
1401 if (mEnd != DEFAULT_RELATIVE) mRight = mEnd;
1402 }
1403
1404 if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
1405 resolveRules(layoutDirection);
1406 }
1407 // This will set the layout direction
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001408 super.resolveLayoutDirection(layoutDirection);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 }
Romain Guy725015a2009-06-23 14:27:34 -07001411
1412 private static class DependencyGraph {
1413 /**
Romain Guy1ab621e2009-06-25 13:31:57 -07001414 * List of all views in the graph.
1415 */
1416 private ArrayList<Node> mNodes = new ArrayList<Node>();
1417
1418 /**
Romain Guy725015a2009-06-23 14:27:34 -07001419 * List of nodes in the graph. Each node is identified by its
1420 * view id (see View#getId()).
1421 */
Romain Guy1ab621e2009-06-25 13:31:57 -07001422 private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001423
1424 /**
1425 * Temporary data structure used to build the list of roots
1426 * for this graph.
1427 */
Romain Guybc5d8762012-01-06 16:40:49 -08001428 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001429
1430 /**
1431 * Clears the graph.
1432 */
1433 void clear() {
Romain Guy1ab621e2009-06-25 13:31:57 -07001434 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001435 final int count = nodes.size();
1436
1437 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001438 nodes.get(i).release();
Romain Guy725015a2009-06-23 14:27:34 -07001439 }
1440 nodes.clear();
1441
Romain Guy1ab621e2009-06-25 13:31:57 -07001442 mKeyNodes.clear();
Romain Guy725015a2009-06-23 14:27:34 -07001443 mRoots.clear();
1444 }
1445
1446 /**
1447 * Adds a view to the graph.
1448 *
1449 * @param view The view to be added as a node to the graph.
1450 */
1451 void add(View view) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001452 final int id = view.getId();
1453 final Node node = Node.acquire(view);
1454
1455 if (id != View.NO_ID) {
1456 mKeyNodes.put(id, node);
1457 }
1458
1459 mNodes.add(node);
Romain Guy725015a2009-06-23 14:27:34 -07001460 }
1461
1462 /**
1463 * Builds a sorted list of views. The sorting order depends on the dependencies
1464 * between the view. For instance, if view C needs view A to be processed first
1465 * and view A needs view B to be processed first, the dependency graph
1466 * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1467 *
1468 * @param sorted The sorted list of views. The length of this array must
1469 * be equal to getChildCount().
1470 * @param rules The list of rules to take into account.
1471 */
1472 void getSortedViews(View[] sorted, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001473 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001474 int index = 0;
1475
Romain Guybc5d8762012-01-06 16:40:49 -08001476 Node node;
1477 while ((node = roots.pollLast()) != null) {
Romain Guy725015a2009-06-23 14:27:34 -07001478 final View view = node.view;
1479 final int key = view.getId();
1480
1481 sorted[index++] = view;
1482
Romain Guybc5d8762012-01-06 16:40:49 -08001483 final HashMap<Node, DependencyGraph> dependents = node.dependents;
1484 for (Node dependent : dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001485 final SparseArray<Node> dependencies = dependent.dependencies;
1486
1487 dependencies.remove(key);
1488 if (dependencies.size() == 0) {
1489 roots.add(dependent);
1490 }
1491 }
1492 }
1493
1494 if (index < sorted.length) {
1495 throw new IllegalStateException("Circular dependencies cannot exist"
1496 + " in RelativeLayout");
1497 }
1498 }
1499
1500 /**
1501 * Finds the roots of the graph. A root is a node with no dependency and
1502 * with [0..n] dependents.
1503 *
1504 * @param rulesFilter The list of rules to consider when building the
1505 * dependencies
1506 *
1507 * @return A list of node, each being a root of the graph
1508 */
Romain Guybc5d8762012-01-06 16:40:49 -08001509 private ArrayDeque<Node> findRoots(int[] rulesFilter) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001510 final SparseArray<Node> keyNodes = mKeyNodes;
1511 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001512 final int count = nodes.size();
1513
1514 // Find roots can be invoked several times, so make sure to clear
1515 // all dependents and dependencies before running the algorithm
1516 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001517 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001518 node.dependents.clear();
1519 node.dependencies.clear();
1520 }
1521
1522 // Builds up the dependents and dependencies for each node of the graph
1523 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001524 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001525
1526 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1527 final int[] rules = layoutParams.mRules;
1528 final int rulesCount = rulesFilter.length;
1529
1530 // Look only the the rules passed in parameter, this way we build only the
1531 // dependencies for a specific set of rules
1532 for (int j = 0; j < rulesCount; j++) {
1533 final int rule = rules[rulesFilter[j]];
1534 if (rule > 0) {
1535 // The node this node depends on
Romain Guy1ab621e2009-06-25 13:31:57 -07001536 final Node dependency = keyNodes.get(rule);
Romain Guyda3003e2009-07-19 19:47:42 -07001537 // Skip unknowns and self dependencies
1538 if (dependency == null || dependency == node) {
Romain Guyb8f8de82009-06-25 12:03:56 -07001539 continue;
1540 }
Romain Guy725015a2009-06-23 14:27:34 -07001541 // Add the current node as a dependent
Romain Guybc5d8762012-01-06 16:40:49 -08001542 dependency.dependents.put(node, this);
Romain Guy725015a2009-06-23 14:27:34 -07001543 // Add a dependency to the current node
1544 node.dependencies.put(rule, dependency);
1545 }
1546 }
1547 }
1548
Romain Guybc5d8762012-01-06 16:40:49 -08001549 final ArrayDeque<Node> roots = mRoots;
Romain Guy725015a2009-06-23 14:27:34 -07001550 roots.clear();
1551
1552 // Finds all the roots in the graph: all nodes with no dependencies
1553 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001554 final Node node = nodes.get(i);
Romain Guybc5d8762012-01-06 16:40:49 -08001555 if (node.dependencies.size() == 0) roots.addLast(node);
Romain Guy725015a2009-06-23 14:27:34 -07001556 }
1557
1558 return roots;
1559 }
1560
1561 /**
1562 * Prints the dependency graph for the specified rules.
1563 *
1564 * @param resources The context's resources to print the ids.
1565 * @param rules The list of rules to take into account.
1566 */
1567 void log(Resources resources, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001568 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001569 for (Node node : roots) {
1570 printNode(resources, node);
1571 }
1572 }
1573
Romain Guy9fffa1e2009-06-24 12:42:43 -07001574 static void printViewId(Resources resources, View view) {
Romain Guy725015a2009-06-23 14:27:34 -07001575 if (view.getId() != View.NO_ID) {
1576 d(LOG_TAG, resources.getResourceEntryName(view.getId()));
1577 } else {
1578 d(LOG_TAG, "NO_ID");
1579 }
1580 }
1581
1582 private static void appendViewId(Resources resources, Node node, StringBuilder buffer) {
1583 if (node.view.getId() != View.NO_ID) {
1584 buffer.append(resources.getResourceEntryName(node.view.getId()));
1585 } else {
1586 buffer.append("NO_ID");
1587 }
1588 }
1589
1590 private static void printNode(Resources resources, Node node) {
1591 if (node.dependents.size() == 0) {
1592 printViewId(resources, node.view);
1593 } else {
Romain Guybc5d8762012-01-06 16:40:49 -08001594 for (Node dependent : node.dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001595 StringBuilder buffer = new StringBuilder();
1596 appendViewId(resources, node, buffer);
1597 printdependents(resources, dependent, buffer);
1598 }
1599 }
1600 }
1601
1602 private static void printdependents(Resources resources, Node node, StringBuilder buffer) {
1603 buffer.append(" -> ");
1604 appendViewId(resources, node, buffer);
1605
1606 if (node.dependents.size() == 0) {
1607 d(LOG_TAG, buffer.toString());
1608 } else {
Romain Guybc5d8762012-01-06 16:40:49 -08001609 for (Node dependent : node.dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001610 StringBuilder subBuffer = new StringBuilder(buffer);
1611 printdependents(resources, dependent, subBuffer);
1612 }
1613 }
1614 }
1615
1616 /**
1617 * A node in the dependency graph. A node is a view, its list of dependencies
1618 * and its list of dependents.
1619 *
1620 * A node with no dependent is considered a root of the graph.
1621 */
1622 static class Node implements Poolable<Node> {
1623 /**
1624 * The view representing this node in the layout.
1625 */
1626 View view;
1627
1628 /**
1629 * The list of dependents for this node; a dependent is a node
1630 * that needs this node to be processed first.
1631 */
Romain Guybc5d8762012-01-06 16:40:49 -08001632 final HashMap<Node, DependencyGraph> dependents = new HashMap<Node, DependencyGraph>();
Romain Guy725015a2009-06-23 14:27:34 -07001633
1634 /**
1635 * The list of dependencies for this node.
1636 */
1637 final SparseArray<Node> dependencies = new SparseArray<Node>();
1638
1639 /*
1640 * START POOL IMPLEMENTATION
1641 */
Romain Guybaac4632009-06-29 14:28:29 -07001642 // The pool is static, so all nodes instances are shared across
1643 // activities, that's why we give it a rather high limit
1644 private static final int POOL_LIMIT = 100;
Romain Guy725015a2009-06-23 14:27:34 -07001645 private static final Pool<Node> sPool = Pools.synchronizedPool(
1646 Pools.finitePool(new PoolableManager<Node>() {
1647 public Node newInstance() {
1648 return new Node();
1649 }
1650
1651 public void onAcquired(Node element) {
1652 }
1653
1654 public void onReleased(Node element) {
1655 }
1656 }, POOL_LIMIT)
1657 );
1658
1659 private Node mNext;
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001660 private boolean mIsPooled;
Romain Guy725015a2009-06-23 14:27:34 -07001661
1662 public void setNextPoolable(Node element) {
1663 mNext = element;
1664 }
1665
1666 public Node getNextPoolable() {
1667 return mNext;
1668 }
1669
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001670 public boolean isPooled() {
1671 return mIsPooled;
1672 }
1673
1674 public void setPooled(boolean isPooled) {
1675 mIsPooled = isPooled;
1676 }
1677
Romain Guy725015a2009-06-23 14:27:34 -07001678 static Node acquire(View view) {
1679 final Node node = sPool.acquire();
1680 node.view = view;
1681
1682 return node;
1683 }
1684
1685 void release() {
1686 view = null;
1687 dependents.clear();
1688 dependencies.clear();
1689
1690 sPool.release(this);
1691 }
1692 /*
1693 * END POOL IMPLEMENTATION
1694 */
1695 }
1696 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001697}