blob: 77ffdaf6efc8b1a0b96c6f595a1faa235cad715a [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;
Adam Powell7da4b732012-12-07 15:28:33 -080032import android.os.Build;
svetoslavganov75986cf2009-05-14 22:28:01 -070033import android.util.AttributeSet;
Svetoslav Ganovabae2a12012-11-27 16:59:37 -080034import android.util.Pools.SimplePool;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070035import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070036import android.view.Gravity;
37import android.view.View;
38import android.view.ViewDebug;
39import android.view.ViewGroup;
40import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080041import android.view.accessibility.AccessibilityNodeInfo;
svetoslavganov75986cf2009-05-14 22:28:01 -070042import android.widget.RemoteViews.RemoteView;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070043
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -080044import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070045import static android.util.Log.d;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
47/**
48 * 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 -080049 * parent.
Romain Guya1f3e4a2009-06-04 15:10:46 -070050 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 * <p>
52 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
53 * position of its children. For example, you cannot have a RelativeLayout whose height is set to
54 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
55 * {@link #ALIGN_PARENT_BOTTOM}.
56 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070057 *
Adam Powell2c8cc972012-12-07 18:04:51 -080058 * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by
59 * a measurement bug that could cause child views to be measured with incorrect
60 * {@link android.view.View.MeasureSpec MeasureSpec} values. (See
61 * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec}
62 * for more details.) This was triggered when a RelativeLayout container was placed in
63 * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view
64 * not equipped to properly measure with the MeasureSpec mode
65 * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout,
66 * this would silently work anyway as RelativeLayout would pass a very large
67 * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p>
68 *
69 * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
70 * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
71 * version 18 or newer will receive the correct behavior</p>
72 *
Scott Main4c359b72012-07-24 15:51:27 -070073 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
74 * Layout</a> guide.</p>
Scott Main41ec6532010-08-19 16:57:07 -070075 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 * <p>
77 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
78 * layout attributes
79 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070080 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 * @attr ref android.R.styleable#RelativeLayout_gravity
82 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
83 */
84@RemoteView
85public class RelativeLayout extends ViewGroup {
Romain Guy725015a2009-06-23 14:27:34 -070086 private static final String LOG_TAG = "RelativeLayout";
87
88 private static final boolean DEBUG_GRAPH = false;
89
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 public static final int TRUE = -1;
91
92 /**
93 * Rule that aligns a child's right edge with another child's left edge.
94 */
95 public static final int LEFT_OF = 0;
96 /**
97 * Rule that aligns a child's left edge with another child's right edge.
98 */
99 public static final int RIGHT_OF = 1;
100 /**
101 * Rule that aligns a child's bottom edge with another child's top edge.
102 */
103 public static final int ABOVE = 2;
104 /**
105 * Rule that aligns a child's top edge with another child's bottom edge.
106 */
107 public static final int BELOW = 3;
108
109 /**
110 * Rule that aligns a child's baseline with another child's baseline.
111 */
112 public static final int ALIGN_BASELINE = 4;
113 /**
114 * Rule that aligns a child's left edge with another child's left edge.
115 */
116 public static final int ALIGN_LEFT = 5;
117 /**
118 * Rule that aligns a child's top edge with another child's top edge.
119 */
120 public static final int ALIGN_TOP = 6;
121 /**
122 * Rule that aligns a child's right edge with another child's right edge.
123 */
124 public static final int ALIGN_RIGHT = 7;
125 /**
126 * Rule that aligns a child's bottom edge with another child's bottom edge.
127 */
128 public static final int ALIGN_BOTTOM = 8;
129
130 /**
131 * Rule that aligns the child's left edge with its RelativeLayout
132 * parent's left edge.
133 */
134 public static final int ALIGN_PARENT_LEFT = 9;
135 /**
136 * Rule that aligns the child's top edge with its RelativeLayout
137 * parent's top edge.
138 */
139 public static final int ALIGN_PARENT_TOP = 10;
140 /**
141 * Rule that aligns the child's right edge with its RelativeLayout
142 * parent's right edge.
143 */
144 public static final int ALIGN_PARENT_RIGHT = 11;
145 /**
146 * Rule that aligns the child's bottom edge with its RelativeLayout
147 * parent's bottom edge.
148 */
149 public static final int ALIGN_PARENT_BOTTOM = 12;
150
151 /**
152 * Rule that centers the child with respect to the bounds of its
153 * RelativeLayout parent.
154 */
155 public static final int CENTER_IN_PARENT = 13;
156 /**
157 * Rule that centers the child horizontally with respect to the
158 * bounds of its RelativeLayout parent.
159 */
160 public static final int CENTER_HORIZONTAL = 14;
161 /**
162 * Rule that centers the child vertically with respect to the
163 * bounds of its RelativeLayout parent.
164 */
165 public static final int CENTER_VERTICAL = 15;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700166 /**
167 * Rule that aligns a child's end edge with another child's start edge.
168 */
169 public static final int START_OF = 16;
170 /**
171 * Rule that aligns a child's start edge with another child's end edge.
172 */
173 public static final int END_OF = 17;
174 /**
175 * Rule that aligns a child's start edge with another child's start edge.
176 */
177 public static final int ALIGN_START = 18;
178 /**
179 * Rule that aligns a child's end edge with another child's end edge.
180 */
181 public static final int ALIGN_END = 19;
182 /**
183 * Rule that aligns the child's start edge with its RelativeLayout
184 * parent's start edge.
185 */
186 public static final int ALIGN_PARENT_START = 20;
187 /**
188 * Rule that aligns the child's end edge with its RelativeLayout
189 * parent's end edge.
190 */
191 public static final int ALIGN_PARENT_END = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700193 private static final int VERB_COUNT = 22;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194
Romain Guybc5d8762012-01-06 16:40:49 -0800195
196 private static final int[] RULES_VERTICAL = {
197 ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
198 };
199
200 private static final int[] RULES_HORIZONTAL = {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700201 LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
Romain Guybc5d8762012-01-06 16:40:49 -0800202 };
203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 private View mBaselineView = null;
205 private boolean mHasBaselineAlignedChild;
206
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700207 private int mGravity = Gravity.START | Gravity.TOP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 private final Rect mContentBounds = new Rect();
209 private final Rect mSelfBounds = new Rect();
210 private int mIgnoreGravity;
211
Romain Guy725015a2009-06-23 14:27:34 -0700212 private SortedSet<View> mTopToBottomLeftToRightSet = null;
213
214 private boolean mDirtyHierarchy;
215 private View[] mSortedHorizontalChildren = new View[0];
216 private View[] mSortedVerticalChildren = new View[0];
217 private final DependencyGraph mGraph = new DependencyGraph();
svetoslavganov75986cf2009-05-14 22:28:01 -0700218
Adam Powell7da4b732012-12-07 15:28:33 -0800219 // Compatibility hack. Old versions of the platform had problems
220 // with MeasureSpec value overflow and RelativeLayout was one source of them.
221 // Some apps came to rely on them. :(
222 private boolean mAllowBrokenMeasureSpecs = false;
Romain Guy76d59a32013-04-08 10:51:35 -0700223 // Compatibility hack. Old versions of the platform would not take
224 // margins and padding into account when generating the height measure spec
225 // for children during the horizontal measure pass.
226 private boolean mMeasureVerticalWithPaddingMargin = false;
Adam Powell7da4b732012-12-07 15:28:33 -0800227
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800228 // A default width used for RTL measure pass
Philip Milneca2e9e12013-04-22 12:44:29 -0700229 /**
230 * Value reduced so as not to interfere with View's measurement spec. flags. See:
231 * {@link View#MEASURED_SIZE_MASK}.
232 * {@link View#MEASURED_STATE_TOO_SMALL}.
233 **/
Philip Milne3c647d22013-04-23 14:31:23 -0700234 private static final int DEFAULT_WIDTH = 0x00010000;
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800235
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 public RelativeLayout(Context context) {
237 super(context);
Romain Guy76d59a32013-04-08 10:51:35 -0700238 queryCompatibilityModes(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 }
240
241 public RelativeLayout(Context context, AttributeSet attrs) {
242 super(context, attrs);
243 initFromAttributes(context, attrs);
Romain Guy76d59a32013-04-08 10:51:35 -0700244 queryCompatibilityModes(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 }
246
247 public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
248 super(context, attrs, defStyle);
249 initFromAttributes(context, attrs);
Romain Guy76d59a32013-04-08 10:51:35 -0700250 queryCompatibilityModes(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 }
252
253 private void initFromAttributes(Context context, AttributeSet attrs) {
254 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout);
255 mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
256 mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
257 a.recycle();
258 }
259
Romain Guy76d59a32013-04-08 10:51:35 -0700260 private void queryCompatibilityModes(Context context) {
261 int version = context.getApplicationInfo().targetSdkVersion;
262 mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
263 mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
264 }
265
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700266 @Override
267 public boolean shouldDelayChildPressedState() {
268 return false;
269 }
270
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 /**
272 * Defines which View is ignored when the gravity is applied. This setting has no
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700273 * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 *
275 * @param viewId The id of the View to be ignored by gravity, or 0 if no View
276 * should be ignored.
277 *
278 * @see #setGravity(int)
279 *
280 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
281 */
282 @android.view.RemotableViewMethod
283 public void setIgnoreGravity(int viewId) {
284 mIgnoreGravity = viewId;
285 }
286
287 /**
Philip Milne1018fb42012-03-13 12:00:04 -0700288 * Describes how the child views are positioned.
289 *
290 * @return the gravity.
291 *
292 * @see #setGravity(int)
293 * @see android.view.Gravity
294 *
295 * @attr ref android.R.styleable#RelativeLayout_gravity
296 */
297 public int getGravity() {
298 return mGravity;
299 }
300
301 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 * Describes how the child views are positioned. Defaults to
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700303 * <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 *
Adam Powell1fec24e2011-09-15 14:19:30 -0700305 * <p>Note that since RelativeLayout considers the positioning of each child
306 * relative to one another to be significant, setting gravity will affect
307 * the positioning of all children as a single unit within the parent.
308 * This happens after children have been relatively positioned.</p>
309 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 * @param gravity See {@link android.view.Gravity}
311 *
312 * @see #setHorizontalGravity(int)
313 * @see #setVerticalGravity(int)
314 *
315 * @attr ref android.R.styleable#RelativeLayout_gravity
316 */
317 @android.view.RemotableViewMethod
318 public void setGravity(int gravity) {
319 if (mGravity != gravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700320 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700321 gravity |= Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 }
323
324 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
325 gravity |= Gravity.TOP;
326 }
327
328 mGravity = gravity;
329 requestLayout();
330 }
331 }
332
333 @android.view.RemotableViewMethod
334 public void setHorizontalGravity(int horizontalGravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700335 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
336 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
337 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 requestLayout();
339 }
340 }
341
342 @android.view.RemotableViewMethod
343 public void setVerticalGravity(int verticalGravity) {
344 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
345 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
346 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
347 requestLayout();
348 }
349 }
350
351 @Override
352 public int getBaseline() {
353 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
354 }
355
356 @Override
Romain Guy725015a2009-06-23 14:27:34 -0700357 public void requestLayout() {
358 super.requestLayout();
359 mDirtyHierarchy = true;
360 }
361
362 private void sortChildren() {
363 int count = getChildCount();
364 if (mSortedVerticalChildren.length != count) mSortedVerticalChildren = new View[count];
365 if (mSortedHorizontalChildren.length != count) mSortedHorizontalChildren = new View[count];
366
367 final DependencyGraph graph = mGraph;
368 graph.clear();
369
370 for (int i = 0; i < count; i++) {
371 final View child = getChildAt(i);
372 graph.add(child);
373 }
374
375 if (DEBUG_GRAPH) {
376 d(LOG_TAG, "=== Sorted vertical children");
Romain Guybc5d8762012-01-06 16:40:49 -0800377 graph.log(getResources(), RULES_VERTICAL);
Romain Guy725015a2009-06-23 14:27:34 -0700378 d(LOG_TAG, "=== Sorted horizontal children");
Romain Guybc5d8762012-01-06 16:40:49 -0800379 graph.log(getResources(), RULES_HORIZONTAL);
Romain Guy725015a2009-06-23 14:27:34 -0700380 }
381
Romain Guybc5d8762012-01-06 16:40:49 -0800382 graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
383 graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
Romain Guy9fffa1e2009-06-24 12:42:43 -0700384
385 if (DEBUG_GRAPH) {
386 d(LOG_TAG, "=== Ordered list of vertical children");
387 for (View view : mSortedVerticalChildren) {
388 DependencyGraph.printViewId(getResources(), view);
389 }
390 d(LOG_TAG, "=== Ordered list of horizontal children");
391 for (View view : mSortedHorizontalChildren) {
392 DependencyGraph.printViewId(getResources(), view);
393 }
394 }
Romain Guy725015a2009-06-23 14:27:34 -0700395 }
396
397 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Romain Guy725015a2009-06-23 14:27:34 -0700399 if (mDirtyHierarchy) {
400 mDirtyHierarchy = false;
401 sortChildren();
402 }
403
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 int myWidth = -1;
405 int myHeight = -1;
406
407 int width = 0;
408 int height = 0;
409
Adam Powell132a7422012-09-30 12:39:56 -0700410 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
411 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
412 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
413 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414
415 // Record our dimensions if they are known;
416 if (widthMode != MeasureSpec.UNSPECIFIED) {
417 myWidth = widthSize;
418 }
419
420 if (heightMode != MeasureSpec.UNSPECIFIED) {
421 myHeight = heightSize;
422 }
423
424 if (widthMode == MeasureSpec.EXACTLY) {
425 width = myWidth;
426 }
427
428 if (heightMode == MeasureSpec.EXACTLY) {
429 height = myHeight;
430 }
431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 mHasBaselineAlignedChild = false;
433
434 View ignore = null;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700435 int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700436 final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
438 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
439
440 int left = Integer.MAX_VALUE;
441 int top = Integer.MAX_VALUE;
442 int right = Integer.MIN_VALUE;
443 int bottom = Integer.MIN_VALUE;
444
Romain Guyf7dabb02009-06-25 14:47:14 -0700445 boolean offsetHorizontalAxis = false;
446 boolean offsetVerticalAxis = false;
447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
449 ignore = findViewById(mIgnoreGravity);
450 }
451
Romain Guyf7dabb02009-06-25 14:47:14 -0700452 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
453 final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
454
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800455 // We need to know our size for doing the correct computation of children positioning in RTL
456 // mode but there is no practical way to get it instead of running the code below.
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800457 // So, instead of running the code twice, we just set the width to a "default display width"
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800458 // before the computation and then, as a last pass, we will update their real position with
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800459 // an offset equals to "DEFAULT_WIDTH - width".
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800460 final int layoutDirection = getLayoutDirection();
461 if (isLayoutRtl() && myWidth == -1) {
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800462 myWidth = DEFAULT_WIDTH;
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800463 }
464
Romain Guye24ef602009-06-25 13:01:55 -0700465 View[] views = mSortedHorizontalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700466 int count = views.length;
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800467
Romain Guy725015a2009-06-23 14:27:34 -0700468 for (int i = 0; i < count; i++) {
469 View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 if (child.getVisibility() != GONE) {
471 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800472 int[] rules = params.getRules(layoutDirection);
Romain Guy95607032009-06-24 14:37:03 -0700473
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800474 applyHorizontalSizeRules(params, myWidth, rules);
Romain Guyf782e602009-06-25 15:26:49 -0700475 measureChildHorizontal(child, params, myWidth, myHeight);
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800476
Romain Guyf7dabb02009-06-25 14:47:14 -0700477 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
478 offsetHorizontalAxis = true;
479 }
Romain Guy725015a2009-06-23 14:27:34 -0700480 }
481 }
482
Romain Guye24ef602009-06-25 13:01:55 -0700483 views = mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700484 count = views.length;
Romain Guyf7dabb02009-06-25 14:47:14 -0700485
Romain Guy725015a2009-06-23 14:27:34 -0700486 for (int i = 0; i < count; i++) {
487 View child = views[i];
488 if (child.getVisibility() != GONE) {
489 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guy95607032009-06-24 14:37:03 -0700490
491 applyVerticalSizeRules(params, myHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 measureChild(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700493 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
494 offsetVerticalAxis = true;
495 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496
Romain Guyf7dabb02009-06-25 14:47:14 -0700497 if (isWrapContentWidth) {
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800498 if (isLayoutRtl()) {
499 width = Math.max(width, myWidth - params.mLeft);
500 } else {
501 width = Math.max(width, params.mRight);
502 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700504
505 if (isWrapContentHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 height = Math.max(height, params.mBottom);
507 }
508
509 if (child != ignore || verticalGravity) {
510 left = Math.min(left, params.mLeft - params.leftMargin);
511 top = Math.min(top, params.mTop - params.topMargin);
512 }
513
514 if (child != ignore || horizontalGravity) {
515 right = Math.max(right, params.mRight + params.rightMargin);
516 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
517 }
518 }
519 }
520
521 if (mHasBaselineAlignedChild) {
Romain Guy725015a2009-06-23 14:27:34 -0700522 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 View child = getChildAt(i);
524 if (child.getVisibility() != GONE) {
525 LayoutParams params = (LayoutParams) child.getLayoutParams();
526 alignBaseline(child, params);
527
528 if (child != ignore || verticalGravity) {
Romain Guy725015a2009-06-23 14:27:34 -0700529 left = Math.min(left, params.mLeft - params.leftMargin);
530 top = Math.min(top, params.mTop - params.topMargin);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 }
532
533 if (child != ignore || horizontalGravity) {
534 right = Math.max(right, params.mRight + params.rightMargin);
535 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
536 }
537 }
538 }
539 }
540
Romain Guyf7dabb02009-06-25 14:47:14 -0700541 if (isWrapContentWidth) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700542 // Width already has left padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 // the right of each child view
544 width += mPaddingRight;
545
546 if (mLayoutParams.width >= 0) {
547 width = Math.max(width, mLayoutParams.width);
548 }
549
550 width = Math.max(width, getSuggestedMinimumWidth());
551 width = resolveSize(width, widthMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700552
553 if (offsetHorizontalAxis) {
Romain Guyd10a5762009-07-28 11:18:14 -0700554 for (int i = 0; i < count; i++) {
Romain Guyf7dabb02009-06-25 14:47:14 -0700555 View child = getChildAt(i);
556 if (child.getVisibility() != GONE) {
557 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700558 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700559 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
560 centerHorizontal(child, params, width);
Romain Guy42460ac2010-01-11 16:46:33 -0800561 } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
562 final int childWidth = child.getMeasuredWidth();
563 params.mLeft = width - mPaddingRight - childWidth;
564 params.mRight = params.mLeft + childWidth;
Romain Guyf7dabb02009-06-25 14:47:14 -0700565 }
566 }
567 }
568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700570
571 if (isWrapContentHeight) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700572 // Height already has top padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 // the bottom of each child view
574 height += mPaddingBottom;
575
576 if (mLayoutParams.height >= 0) {
577 height = Math.max(height, mLayoutParams.height);
578 }
579
580 height = Math.max(height, getSuggestedMinimumHeight());
581 height = resolveSize(height, heightMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700582
583 if (offsetVerticalAxis) {
584 for (int i = 0; i < count; i++) {
585 View child = getChildAt(i);
586 if (child.getVisibility() != GONE) {
587 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700588 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700589 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
590 centerVertical(child, params, height);
Romain Guy42460ac2010-01-11 16:46:33 -0800591 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
592 final int childHeight = child.getMeasuredHeight();
593 params.mTop = height - mPaddingBottom - childHeight;
594 params.mBottom = params.mTop + childHeight;
Romain Guyf7dabb02009-06-25 14:47:14 -0700595 }
596 }
597 }
598 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 }
600
601 if (horizontalGravity || verticalGravity) {
602 final Rect selfBounds = mSelfBounds;
603 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
604 height - mPaddingBottom);
605
606 final Rect contentBounds = mContentBounds;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700607 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
Fabrice Di Meglioc0053222011-06-13 12:16:51 -0700608 layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609
610 final int horizontalOffset = contentBounds.left - left;
611 final int verticalOffset = contentBounds.top - top;
612 if (horizontalOffset != 0 || verticalOffset != 0) {
Romain Guy725015a2009-06-23 14:27:34 -0700613 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 View child = getChildAt(i);
615 if (child.getVisibility() != GONE && child != ignore) {
616 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guyd10a5762009-07-28 11:18:14 -0700617 if (horizontalGravity) {
618 params.mLeft += horizontalOffset;
619 params.mRight += horizontalOffset;
620 }
621 if (verticalGravity) {
622 params.mTop += verticalOffset;
623 params.mBottom += verticalOffset;
624 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 }
626 }
627 }
628 }
629
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800630 if (isLayoutRtl()) {
631 final int offsetWidth = myWidth - width;
632 for (int i = 0; i < count; i++) {
633 View child = getChildAt(i);
634 if (child.getVisibility() != GONE) {
635 LayoutParams params = (LayoutParams) child.getLayoutParams();
636 params.mLeft -= offsetWidth;
637 params.mRight -= offsetWidth;
638 }
639 }
640
641 }
642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 setMeasuredDimension(width, height);
644 }
645
646 private void alignBaseline(View child, LayoutParams params) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700647 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700648 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE);
650
651 if (anchorBaseline != -1) {
652 LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE);
653 if (anchorParams != null) {
654 int offset = anchorParams.mTop + anchorBaseline;
655 int baseline = child.getBaseline();
656 if (baseline != -1) {
657 offset -= baseline;
658 }
659 int height = params.mBottom - params.mTop;
660 params.mTop = offset;
661 params.mBottom = params.mTop + height;
662 }
663 }
664
665 if (mBaselineView == null) {
666 mBaselineView = child;
667 } else {
668 LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams();
669 if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) {
670 mBaselineView = child;
671 }
672 }
673 }
674
675 /**
676 * Measure a child. The child should have left, top, right and bottom information
677 * stored in its LayoutParams. If any of these values is -1 it means that the view
678 * can extend up to the corresponding edge.
679 *
680 * @param child Child to measure
681 * @param params LayoutParams associated with child
682 * @param myWidth Width of the the RelativeLayout
683 * @param myHeight Height of the RelativeLayout
684 */
Romain Guy725015a2009-06-23 14:27:34 -0700685 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
687 params.mBottom, params.height,
688 params.topMargin, params.bottomMargin,
689 mPaddingTop, mPaddingBottom,
690 myHeight);
Romain Guye8fb03c2013-04-25 11:40:45 -0700691 switch (MeasureSpec.getMode(childHeightMeasureSpec)) {
692 case MeasureSpec.AT_MOST:
693 if (child.getMeasuredHeight() <= MeasureSpec.getSize(childHeightMeasureSpec)) {
694 return;
695 }
696 break;
697 case MeasureSpec.EXACTLY:
698 if (child.getMeasuredHeight() == MeasureSpec.getSize(childHeightMeasureSpec)) {
699 return;
700 }
701 break;
702 }
703 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
704 params.mRight, params.width,
705 params.leftMargin, params.rightMargin,
706 mPaddingLeft, mPaddingRight,
707 myWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
709 }
710
Romain Guyf782e602009-06-25 15:26:49 -0700711 private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
Romain Guy95607032009-06-24 14:37:03 -0700712 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
713 params.mRight, params.width,
714 params.leftMargin, params.rightMargin,
715 mPaddingLeft, mPaddingRight,
716 myWidth);
Romain Guy76d59a32013-04-08 10:51:35 -0700717 int maxHeight = myHeight;
718 if (mMeasureVerticalWithPaddingMargin) {
719 maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom -
720 params.topMargin - params.bottomMargin);
721 }
Romain Guyf782e602009-06-25 15:26:49 -0700722 int childHeightMeasureSpec;
Adam Powell7da4b732012-12-07 15:28:33 -0800723 if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
Romain Guyf16c7a92013-02-11 17:43:59 -0800724 if (params.height >= 0) {
725 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
726 params.height, MeasureSpec.EXACTLY);
727 } else {
728 // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement
729 // is code for, "we got an unspecified mode in the RelativeLayout's measurespec."
730 // Carry it forward.
731 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
732 }
Adam Powell132a7422012-09-30 12:39:56 -0700733 } else if (params.width == LayoutParams.MATCH_PARENT) {
Romain Guy76d59a32013-04-08 10:51:35 -0700734 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
Romain Guyf782e602009-06-25 15:26:49 -0700735 } else {
Romain Guy76d59a32013-04-08 10:51:35 -0700736 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
Romain Guyf782e602009-06-25 15:26:49 -0700737 }
Romain Guy725015a2009-06-23 14:27:34 -0700738 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
739 }
740
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 /**
742 * Get a measure spec that accounts for all of the constraints on this view.
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700743 * This includes size constraints imposed by the RelativeLayout as well as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 * the View's desired dimension.
745 *
746 * @param childStart The left or top field of the child's layout params
747 * @param childEnd The right or bottom field of the child's layout params
748 * @param childSize The child's desired size (the width or height field of
749 * the child's layout params)
750 * @param startMargin The left or top margin
751 * @param endMargin The right or bottom margin
752 * @param startPadding mPaddingLeft or mPaddingTop
753 * @param endPadding mPaddingRight or mPaddingBottom
754 * @param mySize The width or height of this view (the RelativeLayout)
755 * @return MeasureSpec for the child
756 */
757 private int getChildMeasureSpec(int childStart, int childEnd,
758 int childSize, int startMargin, int endMargin, int startPadding,
759 int endPadding, int mySize) {
Adam Powell7da4b732012-12-07 15:28:33 -0800760 if (mySize < 0 && !mAllowBrokenMeasureSpecs) {
Romain Guyf16c7a92013-02-11 17:43:59 -0800761 if (childSize >= 0) {
762 return MeasureSpec.makeMeasureSpec(childSize, MeasureSpec.EXACTLY);
763 }
Adam Powell132a7422012-09-30 12:39:56 -0700764 // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement
765 // is code for, "we got an unspecified mode in the RelativeLayout's measurespec."
766 // Carry it forward.
767 return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
768 }
769
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 int childSpecMode = 0;
771 int childSpecSize = 0;
772
773 // Figure out start and end bounds.
774 int tempStart = childStart;
775 int tempEnd = childEnd;
776
777 // If the view did not express a layout constraint for an edge, use
778 // view's margins and our padding
779 if (tempStart < 0) {
780 tempStart = startPadding + startMargin;
781 }
782 if (tempEnd < 0) {
783 tempEnd = mySize - endPadding - endMargin;
784 }
785
786 // Figure out maximum size available to this view
787 int maxAvailable = tempEnd - tempStart;
788
789 if (childStart >= 0 && childEnd >= 0) {
790 // Constraints fixed both edges, so child must be an exact size
791 childSpecMode = MeasureSpec.EXACTLY;
792 childSpecSize = maxAvailable;
793 } else {
794 if (childSize >= 0) {
795 // Child wanted an exact size. Give as much as possible
796 childSpecMode = MeasureSpec.EXACTLY;
797
798 if (maxAvailable >= 0) {
799 // We have a maxmum size in this dimension.
800 childSpecSize = Math.min(maxAvailable, childSize);
801 } else {
802 // We can grow in this dimension.
803 childSpecSize = childSize;
804 }
Romain Guy980a9382010-01-08 15:06:28 -0800805 } else if (childSize == LayoutParams.MATCH_PARENT) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700806 // Child wanted to be as big as possible. Give all available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 // space
808 childSpecMode = MeasureSpec.EXACTLY;
809 childSpecSize = maxAvailable;
810 } else if (childSize == LayoutParams.WRAP_CONTENT) {
811 // Child wants to wrap content. Use AT_MOST
812 // to communicate available space if we know
813 // our max size
814 if (maxAvailable >= 0) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700815 // We have a maximum size in this dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 childSpecMode = MeasureSpec.AT_MOST;
817 childSpecSize = maxAvailable;
818 } else {
819 // We can grow in this dimension. Child can be as big as it
820 // wants
821 childSpecMode = MeasureSpec.UNSPECIFIED;
822 childSpecSize = 0;
823 }
824 }
825 }
826
827 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
828 }
829
Romain Guyf7dabb02009-06-25 14:47:14 -0700830 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
831 boolean wrapContent) {
832
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700833 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700834 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835
836 if (params.mLeft < 0 && params.mRight >= 0) {
837 // Right is fixed, but left varies
838 params.mLeft = params.mRight - child.getMeasuredWidth();
839 } else if (params.mLeft >= 0 && params.mRight < 0) {
840 // Left is fixed, but right varies
841 params.mRight = params.mLeft + child.getMeasuredWidth();
842 } else if (params.mLeft < 0 && params.mRight < 0) {
843 // Both left and right vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700844 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
845 if (!wrapContent) {
846 centerHorizontal(child, params, myWidth);
847 } else {
848 params.mLeft = mPaddingLeft + params.leftMargin;
849 params.mRight = params.mLeft + child.getMeasuredWidth();
850 }
851 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 } else {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700853 // This is the default case. For RTL we start from the right and for LTR we start
854 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
855 if (isLayoutRtl()) {
856 params.mRight = myWidth - mPaddingRight- params.rightMargin;
857 params.mLeft = params.mRight - child.getMeasuredWidth();
858 } else {
859 params.mLeft = mPaddingLeft + params.leftMargin;
860 params.mRight = params.mLeft + child.getMeasuredWidth();
861 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 }
863 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700864 return rules[ALIGN_PARENT_END] != 0;
Romain Guy725015a2009-06-23 14:27:34 -0700865 }
866
Romain Guyf7dabb02009-06-25 14:47:14 -0700867 private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
868 boolean wrapContent) {
869
Romain Guy725015a2009-06-23 14:27:34 -0700870 int[] rules = params.getRules();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871
872 if (params.mTop < 0 && params.mBottom >= 0) {
873 // Bottom is fixed, but top varies
874 params.mTop = params.mBottom - child.getMeasuredHeight();
875 } else if (params.mTop >= 0 && params.mBottom < 0) {
876 // Top is fixed, but bottom varies
877 params.mBottom = params.mTop + child.getMeasuredHeight();
878 } else if (params.mTop < 0 && params.mBottom < 0) {
879 // Both top and bottom vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700880 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
881 if (!wrapContent) {
882 centerVertical(child, params, myHeight);
883 } else {
884 params.mTop = mPaddingTop + params.topMargin;
885 params.mBottom = params.mTop + child.getMeasuredHeight();
886 }
887 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 } else {
889 params.mTop = mPaddingTop + params.topMargin;
890 params.mBottom = params.mTop + child.getMeasuredHeight();
891 }
892 }
Romain Guy42460ac2010-01-11 16:46:33 -0800893 return rules[ALIGN_PARENT_BOTTOM] != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 }
895
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800896 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 RelativeLayout.LayoutParams anchorParams;
898
899 // -1 indicated a "soft requirement" in that direction. For example:
900 // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right
901 // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left
902 // left=10, right=20 means the left and right ends are both fixed
903 childParams.mLeft = -1;
904 childParams.mRight = -1;
905
906 anchorParams = getRelatedViewParams(rules, LEFT_OF);
907 if (anchorParams != null) {
908 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
909 childParams.rightMargin);
910 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
911 if (myWidth >= 0) {
912 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 }
914 }
915
916 anchorParams = getRelatedViewParams(rules, RIGHT_OF);
917 if (anchorParams != null) {
918 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
919 childParams.leftMargin);
920 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
921 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
922 }
923
924 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
925 if (anchorParams != null) {
926 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
927 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
928 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
929 }
930
931 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
932 if (anchorParams != null) {
933 childParams.mRight = anchorParams.mRight - childParams.rightMargin;
934 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
935 if (myWidth >= 0) {
936 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 }
938 }
939
Romain Guye8fb03c2013-04-25 11:40:45 -0700940 if (rules[ALIGN_PARENT_LEFT] != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
942 }
943
Romain Guye8fb03c2013-04-25 11:40:45 -0700944 if (rules[ALIGN_PARENT_RIGHT] != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 if (myWidth >= 0) {
946 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
948 }
Romain Guy725015a2009-06-23 14:27:34 -0700949 }
950
951 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) {
952 int[] rules = childParams.getRules();
953 RelativeLayout.LayoutParams anchorParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954
955 childParams.mTop = -1;
956 childParams.mBottom = -1;
957
958 anchorParams = getRelatedViewParams(rules, ABOVE);
959 if (anchorParams != null) {
960 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
961 childParams.bottomMargin);
962 } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
963 if (myHeight >= 0) {
964 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 }
966 }
967
968 anchorParams = getRelatedViewParams(rules, BELOW);
969 if (anchorParams != null) {
970 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
971 childParams.topMargin);
972 } else if (childParams.alignWithParent && rules[BELOW] != 0) {
973 childParams.mTop = mPaddingTop + childParams.topMargin;
974 }
975
976 anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
977 if (anchorParams != null) {
978 childParams.mTop = anchorParams.mTop + childParams.topMargin;
979 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
980 childParams.mTop = mPaddingTop + childParams.topMargin;
981 }
982
983 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
984 if (anchorParams != null) {
985 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
986 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
987 if (myHeight >= 0) {
988 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 }
990 }
991
Romain Guye8fb03c2013-04-25 11:40:45 -0700992 if (rules[ALIGN_PARENT_TOP] != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 childParams.mTop = mPaddingTop + childParams.topMargin;
994 }
995
Romain Guye8fb03c2013-04-25 11:40:45 -0700996 if (rules[ALIGN_PARENT_BOTTOM] != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 if (myHeight >= 0) {
998 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 }
1000 }
1001
1002 if (rules[ALIGN_BASELINE] != 0) {
1003 mHasBaselineAlignedChild = true;
1004 }
1005 }
1006
1007 private View getRelatedView(int[] rules, int relation) {
1008 int id = rules[relation];
1009 if (id != 0) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001010 DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
Romain Guya0fd1d72009-06-24 14:25:43 -07001011 if (node == null) return null;
1012 View v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013
1014 // Find the first non-GONE view up the chain
1015 while (v.getVisibility() == View.GONE) {
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001016 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
Romain Guy1ab621e2009-06-25 13:31:57 -07001017 node = mGraph.mKeyNodes.get((rules[relation]));
Romain Guya0fd1d72009-06-24 14:25:43 -07001018 if (node == null) return null;
1019 v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 }
1021
1022 return v;
1023 }
1024
1025 return null;
1026 }
1027
1028 private LayoutParams getRelatedViewParams(int[] rules, int relation) {
1029 View v = getRelatedView(rules, relation);
1030 if (v != null) {
1031 ViewGroup.LayoutParams params = v.getLayoutParams();
1032 if (params instanceof LayoutParams) {
1033 return (LayoutParams) v.getLayoutParams();
1034 }
1035 }
1036 return null;
1037 }
1038
1039 private int getRelatedViewBaseline(int[] rules, int relation) {
1040 View v = getRelatedView(rules, relation);
1041 if (v != null) {
1042 return v.getBaseline();
1043 }
1044 return -1;
1045 }
1046
Romain Guyf16c7a92013-02-11 17:43:59 -08001047 private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 int childWidth = child.getMeasuredWidth();
1049 int left = (myWidth - childWidth) / 2;
1050
1051 params.mLeft = left;
1052 params.mRight = left + childWidth;
1053 }
1054
Romain Guyf16c7a92013-02-11 17:43:59 -08001055 private static void centerVertical(View child, LayoutParams params, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 int childHeight = child.getMeasuredHeight();
1057 int top = (myHeight - childHeight) / 2;
1058
1059 params.mTop = top;
1060 params.mBottom = top + childHeight;
1061 }
1062
1063 @Override
1064 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1065 // The layout has actually already been performed and the positions
1066 // cached. Apply the cached values to the children.
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001067 final int count = getChildCount();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068
1069 for (int i = 0; i < count; i++) {
1070 View child = getChildAt(i);
1071 if (child.getVisibility() != GONE) {
1072 RelativeLayout.LayoutParams st =
1073 (RelativeLayout.LayoutParams) child.getLayoutParams();
1074 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 }
1076 }
1077 }
1078
1079 @Override
1080 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1081 return new RelativeLayout.LayoutParams(getContext(), attrs);
1082 }
1083
1084 /**
1085 * Returns a set of layout parameters with a width of
1086 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1087 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1088 */
1089 @Override
1090 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1091 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1092 }
1093
1094 // Override to allow type-checking of LayoutParams.
1095 @Override
1096 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1097 return p instanceof RelativeLayout.LayoutParams;
1098 }
1099
1100 @Override
1101 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1102 return new LayoutParams(p);
1103 }
1104
svetoslavganov75986cf2009-05-14 22:28:01 -07001105 @Override
1106 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1107 if (mTopToBottomLeftToRightSet == null) {
1108 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1109 }
1110
1111 // sort children top-to-bottom and left-to-right
1112 for (int i = 0, count = getChildCount(); i < count; i++) {
1113 mTopToBottomLeftToRightSet.add(getChildAt(i));
1114 }
1115
1116 for (View view : mTopToBottomLeftToRightSet) {
Svetoslav Ganov0b0a41d2011-09-07 18:06:03 -07001117 if (view.getVisibility() == View.VISIBLE
1118 && view.dispatchPopulateAccessibilityEvent(event)) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001119 mTopToBottomLeftToRightSet.clear();
1120 return true;
1121 }
1122 }
1123
1124 mTopToBottomLeftToRightSet.clear();
1125 return false;
1126 }
1127
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001128 @Override
1129 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1130 super.onInitializeAccessibilityEvent(event);
1131 event.setClassName(RelativeLayout.class.getName());
1132 }
1133
1134 @Override
1135 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1136 super.onInitializeAccessibilityNodeInfo(info);
1137 info.setClassName(RelativeLayout.class.getName());
1138 }
1139
svetoslavganov75986cf2009-05-14 22:28:01 -07001140 /**
1141 * Compares two views in left-to-right and top-to-bottom fashion.
1142 */
1143 private class TopToBottomLeftToRightComparator implements Comparator<View> {
1144 public int compare(View first, View second) {
1145 // top - bottom
1146 int topDifference = first.getTop() - second.getTop();
1147 if (topDifference != 0) {
1148 return topDifference;
1149 }
1150 // left - right
1151 int leftDifference = first.getLeft() - second.getLeft();
1152 if (leftDifference != 0) {
1153 return leftDifference;
1154 }
1155 // break tie by height
1156 int heightDiference = first.getHeight() - second.getHeight();
1157 if (heightDiference != 0) {
1158 return heightDiference;
1159 }
1160 // break tie by width
1161 int widthDiference = first.getWidth() - second.getWidth();
1162 if (widthDiference != 0) {
1163 return widthDiference;
1164 }
1165 return 0;
1166 }
1167 }
1168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 /**
1170 * Per-child layout information associated with RelativeLayout.
1171 *
1172 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1173 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1174 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1175 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1176 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1177 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1178 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1179 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1180 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1181 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1182 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1183 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1184 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1185 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1186 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1187 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1188 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001189 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1190 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1191 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1192 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1193 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1194 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 */
1196 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001197 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001198 @ViewDebug.IntToString(from = ABOVE, to = "above"),
1199 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
1200 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
1201 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"),
1202 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1203 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"),
1204 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"),
1205 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"),
1206 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"),
1207 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"),
1208 @ViewDebug.IntToString(from = BELOW, to = "below"),
1209 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"),
1210 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"),
1211 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"),
1212 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"),
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001213 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"),
1214 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"),
1215 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"),
1216 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"),
1217 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"),
1218 @ViewDebug.IntToString(from = START_OF, to = "startOf"),
1219 @ViewDebug.IntToString(from = END_OF, to = "endOf")
The Android Open Source Project10592532009-03-18 17:39:46 -07001220 }, mapping = {
1221 @ViewDebug.IntToString(from = TRUE, to = "true"),
Romain Guya1f3e4a2009-06-04 15:10:46 -07001222 @ViewDebug.IntToString(from = 0, to = "false/NO_ID")
The Android Open Source Project10592532009-03-18 17:39:46 -07001223 })
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 private int[] mRules = new int[VERB_COUNT];
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001226 private int[] mInitialRules = new int[VERB_COUNT];
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 private int mLeft, mTop, mRight, mBottom;
1229
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001230 private int mStart = DEFAULT_MARGIN_RELATIVE;
1231 private int mEnd = DEFAULT_MARGIN_RELATIVE;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001232
1233 private boolean mRulesChanged = false;
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001234 private boolean mIsRtlCompatibilityMode = false;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001235
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 /**
1237 * When true, uses the parent as the anchor if the anchor doesn't exist or if
1238 * the anchor's visibility is GONE.
1239 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001240 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 public boolean alignWithParent;
1242
1243 public LayoutParams(Context c, AttributeSet attrs) {
1244 super(c, attrs);
1245
1246 TypedArray a = c.obtainStyledAttributes(attrs,
1247 com.android.internal.R.styleable.RelativeLayout_Layout);
1248
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001249 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
1250 mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
1251 !c.getApplicationInfo().hasRtlSupport());
1252
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 final int[] rules = mRules;
Romain Guyf16c7a92013-02-11 17:43:59 -08001254 //noinspection MismatchedReadAndWriteOfArray
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001255 final int[] initialRules = mInitialRules;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256
1257 final int N = a.getIndexCount();
1258 for (int i = 0; i < N; i++) {
1259 int attr = a.getIndex(i);
1260 switch (attr) {
1261 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1262 alignWithParent = a.getBoolean(attr, false);
1263 break;
1264 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1265 rules[LEFT_OF] = a.getResourceId(attr, 0);
1266 break;
1267 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1268 rules[RIGHT_OF] = a.getResourceId(attr, 0);
1269 break;
1270 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1271 rules[ABOVE] = a.getResourceId(attr, 0);
1272 break;
1273 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1274 rules[BELOW] = a.getResourceId(attr, 0);
1275 break;
1276 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1277 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1278 break;
1279 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1280 rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1281 break;
1282 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1283 rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1284 break;
1285 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1286 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1287 break;
1288 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1289 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1290 break;
1291 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1292 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1293 break;
1294 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1295 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1296 break;
1297 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1298 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1299 break;
1300 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1301 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1302 break;
1303 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1304 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1305 break;
1306 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1307 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1308 break;
1309 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1310 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1311 break;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001312 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1313 rules[START_OF] = a.getResourceId(attr, 0);
1314 break;
1315 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1316 rules[END_OF] = a.getResourceId(attr, 0);
1317 break;
1318 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1319 rules[ALIGN_START] = a.getResourceId(attr, 0);
1320 break;
1321 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1322 rules[ALIGN_END] = a.getResourceId(attr, 0);
1323 break;
1324 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1325 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1326 break;
1327 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1328 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1329 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 }
1331 }
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001332 mRulesChanged = true;
Romain Guyf16c7a92013-02-11 17:43:59 -08001333 System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001334
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335 a.recycle();
1336 }
1337
1338 public LayoutParams(int w, int h) {
1339 super(w, h);
1340 }
1341
1342 /**
1343 * {@inheritDoc}
1344 */
1345 public LayoutParams(ViewGroup.LayoutParams source) {
1346 super(source);
1347 }
1348
1349 /**
1350 * {@inheritDoc}
1351 */
1352 public LayoutParams(ViewGroup.MarginLayoutParams source) {
1353 super(source);
1354 }
1355
1356 @Override
1357 public String debug(String output) {
1358 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1359 ", height=" + sizeToString(height) + " }";
1360 }
1361
1362 /**
1363 * Adds a layout rule to be interpreted by the RelativeLayout. This
1364 * method should only be used for constraints that don't refer to another sibling
1365 * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE}
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001366 * 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 -08001367 * {@link #addRule(int, int)} instead.
1368 *
1369 * @param verb One of the verbs defined by
1370 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1371 * ALIGN_WITH_PARENT_LEFT.
1372 * @see #addRule(int, int)
1373 */
1374 public void addRule(int verb) {
1375 mRules[verb] = TRUE;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001376 mInitialRules[verb] = TRUE;
1377 mRulesChanged = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378 }
1379
1380 /**
1381 * Adds a layout rule to be interpreted by the RelativeLayout. Use this for
1382 * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
1383 * value (VISIBLE).
1384 *
1385 * @param verb One of the verbs defined by
1386 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1387 * ALIGN_WITH_PARENT_LEFT.
1388 * @param anchor The id of another view to use as an anchor,
1389 * or a boolean value(represented as {@link RelativeLayout#TRUE})
1390 * for true or 0 for false). For verbs that don't refer to another sibling
1391 * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
1392 * @see #addRule(int)
1393 */
1394 public void addRule(int verb, int anchor) {
1395 mRules[verb] = anchor;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001396 mInitialRules[verb] = anchor;
1397 mRulesChanged = true;
1398 }
1399
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001400 /**
1401 * Removes a layout rule to be interpreted by the RelativeLayout.
1402 *
1403 * @param verb One of the verbs defined by
1404 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1405 * ALIGN_WITH_PARENT_LEFT.
1406 * @see #addRule(int)
1407 * @see #addRule(int, int)
1408 */
1409 public void removeRule(int verb) {
1410 mRules[verb] = 0;
1411 mInitialRules[verb] = 0;
1412 mRulesChanged = true;
1413 }
1414
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001415 private boolean hasRelativeRules() {
1416 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1417 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1418 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1419 }
1420
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001421 // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
1422 // or not.
1423 //
1424 // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
1425 // predominance over any "start/end" rules that could have been defined. A special case:
1426 // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
1427 // resolve those "start"/"end" rules to "left"/"right" respectively.
1428 //
1429 // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
1430 // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
1431 //
1432 // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
1433 // only the "left"/"right" rules at the end.
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001434 private void resolveRules(int layoutDirection) {
1435 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001436
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001437 // Reset to initial state
Romain Guyf16c7a92013-02-11 17:43:59 -08001438 System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001439
1440 // Apply rules depending on direction and if we are in RTL compatibility mode
1441 if (mIsRtlCompatibilityMode) {
1442 if (mRules[ALIGN_START] != 0) {
1443 if (mRules[ALIGN_LEFT] == 0) {
1444 // "left" rule is not defined but "start" rule is: use the "start" rule as
1445 // the "left" rule
1446 mRules[ALIGN_LEFT] = mRules[ALIGN_START];
1447 }
1448 mRules[ALIGN_START] = 0;
1449 }
1450
1451 if (mRules[ALIGN_END] != 0) {
1452 if (mRules[ALIGN_RIGHT] == 0) {
1453 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1454 // "right" rule
1455 mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
1456 }
1457 mRules[ALIGN_END] = 0;
1458 }
1459
1460 if (mRules[START_OF] != 0) {
1461 if (mRules[LEFT_OF] == 0) {
1462 // "left" rule is not defined but "start" rule is: use the "start" rule as
1463 // the "left" rule
1464 mRules[LEFT_OF] = mRules[START_OF];
1465 }
1466 mRules[START_OF] = 0;
1467 }
1468
1469 if (mRules[END_OF] != 0) {
1470 if (mRules[RIGHT_OF] == 0) {
1471 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1472 // "right" rule
1473 mRules[RIGHT_OF] = mRules[END_OF];
1474 }
1475 mRules[END_OF] = 0;
1476 }
1477
1478 if (mRules[ALIGN_PARENT_START] != 0) {
1479 if (mRules[ALIGN_PARENT_LEFT] == 0) {
1480 // "left" rule is not defined but "start" rule is: use the "start" rule as
1481 // the "left" rule
1482 mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1483 }
1484 mRules[ALIGN_PARENT_START] = 0;
1485 }
1486
1487 if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1488 if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1489 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1490 // "right" rule
1491 mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1492 }
1493 mRules[ALIGN_PARENT_END] = 0;
1494 }
1495 } else {
1496 // JB MR1+ case
1497 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
1498 (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
1499 // "start"/"end" rules take precedence over "left"/"right" rules
1500 mRules[ALIGN_LEFT] = 0;
1501 mRules[ALIGN_RIGHT] = 0;
1502 }
1503 if (mRules[ALIGN_START] != 0) {
1504 // "start" rule resolved to "left" or "right" depending on the direction
1505 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1506 mRules[ALIGN_START] = 0;
1507 }
1508 if (mRules[ALIGN_END] != 0) {
1509 // "end" rule resolved to "left" or "right" depending on the direction
1510 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1511 mRules[ALIGN_END] = 0;
1512 }
1513
1514 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
1515 (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
1516 // "start"/"end" rules take precedence over "left"/"right" rules
1517 mRules[LEFT_OF] = 0;
1518 mRules[RIGHT_OF] = 0;
1519 }
1520 if (mRules[START_OF] != 0) {
1521 // "start" rule resolved to "left" or "right" depending on the direction
1522 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1523 mRules[START_OF] = 0;
1524 }
1525 if (mRules[END_OF] != 0) {
1526 // "end" rule resolved to "left" or "right" depending on the direction
1527 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1528 mRules[END_OF] = 0;
1529 }
1530
1531 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
1532 (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
1533 // "start"/"end" rules take precedence over "left"/"right" rules
1534 mRules[ALIGN_PARENT_LEFT] = 0;
1535 mRules[ALIGN_PARENT_RIGHT] = 0;
1536 }
1537 if (mRules[ALIGN_PARENT_START] != 0) {
1538 // "start" rule resolved to "left" or "right" depending on the direction
1539 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1540 mRules[ALIGN_PARENT_START] = 0;
1541 }
1542 if (mRules[ALIGN_PARENT_END] != 0) {
1543 // "end" rule resolved to "left" or "right" depending on the direction
1544 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1545 mRules[ALIGN_PARENT_END] = 0;
1546 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001547 }
1548 mRulesChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549 }
1550
1551 /**
1552 * Retrieves a complete list of all supported rules, where the index is the rule
1553 * verb, and the element value is the value specified, or "false" if it was never
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001554 * set. If there are relative rules defined (*_START / *_END), they will be resolved
1555 * depending on the layout direction.
1556 *
1557 * @param layoutDirection the direction of the layout.
1558 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
1559 * or {@link View#LAYOUT_DIRECTION_RTL}
1560 * @return the supported rules
1561 * @see #addRule(int, int)
1562 *
1563 * @hide
1564 */
1565 public int[] getRules(int layoutDirection) {
1566 if (hasRelativeRules() &&
1567 (mRulesChanged || layoutDirection != getLayoutDirection())) {
1568 resolveRules(layoutDirection);
1569 if (layoutDirection != getLayoutDirection()) {
1570 setLayoutDirection(layoutDirection);
1571 }
1572 }
1573 return mRules;
1574 }
1575
1576 /**
1577 * Retrieves a complete list of all supported rules, where the index is the rule
1578 * verb, and the element value is the value specified, or "false" if it was never
1579 * set. There will be no resolution of relative rules done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 *
1581 * @return the supported rules
1582 * @see #addRule(int, int)
1583 */
1584 public int[] getRules() {
1585 return mRules;
1586 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001587
1588 @Override
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001589 public void resolveLayoutDirection(int layoutDirection) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001590 final boolean isLayoutRtl = isLayoutRtl();
1591 if (isLayoutRtl) {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001592 if (mStart != DEFAULT_MARGIN_RELATIVE) mRight = mStart;
1593 if (mEnd != DEFAULT_MARGIN_RELATIVE) mLeft = mEnd;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001594 } else {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001595 if (mStart != DEFAULT_MARGIN_RELATIVE) mLeft = mStart;
1596 if (mEnd != DEFAULT_MARGIN_RELATIVE) mRight = mEnd;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001597 }
1598
1599 if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
1600 resolveRules(layoutDirection);
1601 }
1602 // This will set the layout direction
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001603 super.resolveLayoutDirection(layoutDirection);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001604 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 }
Romain Guy725015a2009-06-23 14:27:34 -07001606
1607 private static class DependencyGraph {
1608 /**
Romain Guy1ab621e2009-06-25 13:31:57 -07001609 * List of all views in the graph.
1610 */
1611 private ArrayList<Node> mNodes = new ArrayList<Node>();
1612
1613 /**
Romain Guy725015a2009-06-23 14:27:34 -07001614 * List of nodes in the graph. Each node is identified by its
1615 * view id (see View#getId()).
1616 */
Romain Guy1ab621e2009-06-25 13:31:57 -07001617 private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001618
1619 /**
1620 * Temporary data structure used to build the list of roots
1621 * for this graph.
1622 */
Romain Guybc5d8762012-01-06 16:40:49 -08001623 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001624
1625 /**
1626 * Clears the graph.
1627 */
1628 void clear() {
Romain Guy1ab621e2009-06-25 13:31:57 -07001629 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001630 final int count = nodes.size();
1631
1632 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001633 nodes.get(i).release();
Romain Guy725015a2009-06-23 14:27:34 -07001634 }
1635 nodes.clear();
1636
Romain Guy1ab621e2009-06-25 13:31:57 -07001637 mKeyNodes.clear();
Romain Guy725015a2009-06-23 14:27:34 -07001638 mRoots.clear();
1639 }
1640
1641 /**
1642 * Adds a view to the graph.
1643 *
1644 * @param view The view to be added as a node to the graph.
1645 */
1646 void add(View view) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001647 final int id = view.getId();
1648 final Node node = Node.acquire(view);
1649
1650 if (id != View.NO_ID) {
1651 mKeyNodes.put(id, node);
1652 }
1653
1654 mNodes.add(node);
Romain Guy725015a2009-06-23 14:27:34 -07001655 }
1656
1657 /**
1658 * Builds a sorted list of views. The sorting order depends on the dependencies
1659 * between the view. For instance, if view C needs view A to be processed first
1660 * and view A needs view B to be processed first, the dependency graph
1661 * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1662 *
1663 * @param sorted The sorted list of views. The length of this array must
1664 * be equal to getChildCount().
1665 * @param rules The list of rules to take into account.
1666 */
1667 void getSortedViews(View[] sorted, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001668 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001669 int index = 0;
1670
Romain Guybc5d8762012-01-06 16:40:49 -08001671 Node node;
1672 while ((node = roots.pollLast()) != null) {
Romain Guy725015a2009-06-23 14:27:34 -07001673 final View view = node.view;
1674 final int key = view.getId();
1675
1676 sorted[index++] = view;
1677
Romain Guybc5d8762012-01-06 16:40:49 -08001678 final HashMap<Node, DependencyGraph> dependents = node.dependents;
1679 for (Node dependent : dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001680 final SparseArray<Node> dependencies = dependent.dependencies;
1681
1682 dependencies.remove(key);
1683 if (dependencies.size() == 0) {
1684 roots.add(dependent);
1685 }
1686 }
1687 }
1688
1689 if (index < sorted.length) {
1690 throw new IllegalStateException("Circular dependencies cannot exist"
1691 + " in RelativeLayout");
1692 }
1693 }
1694
1695 /**
1696 * Finds the roots of the graph. A root is a node with no dependency and
1697 * with [0..n] dependents.
1698 *
1699 * @param rulesFilter The list of rules to consider when building the
1700 * dependencies
1701 *
1702 * @return A list of node, each being a root of the graph
1703 */
Romain Guybc5d8762012-01-06 16:40:49 -08001704 private ArrayDeque<Node> findRoots(int[] rulesFilter) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001705 final SparseArray<Node> keyNodes = mKeyNodes;
1706 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001707 final int count = nodes.size();
1708
1709 // Find roots can be invoked several times, so make sure to clear
1710 // all dependents and dependencies before running the algorithm
1711 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001712 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001713 node.dependents.clear();
1714 node.dependencies.clear();
1715 }
1716
1717 // Builds up the dependents and dependencies for each node of the graph
1718 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001719 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001720
1721 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1722 final int[] rules = layoutParams.mRules;
1723 final int rulesCount = rulesFilter.length;
1724
1725 // Look only the the rules passed in parameter, this way we build only the
1726 // dependencies for a specific set of rules
1727 for (int j = 0; j < rulesCount; j++) {
1728 final int rule = rules[rulesFilter[j]];
1729 if (rule > 0) {
1730 // The node this node depends on
Romain Guy1ab621e2009-06-25 13:31:57 -07001731 final Node dependency = keyNodes.get(rule);
Romain Guyda3003e2009-07-19 19:47:42 -07001732 // Skip unknowns and self dependencies
1733 if (dependency == null || dependency == node) {
Romain Guyb8f8de82009-06-25 12:03:56 -07001734 continue;
1735 }
Romain Guy725015a2009-06-23 14:27:34 -07001736 // Add the current node as a dependent
Romain Guybc5d8762012-01-06 16:40:49 -08001737 dependency.dependents.put(node, this);
Romain Guy725015a2009-06-23 14:27:34 -07001738 // Add a dependency to the current node
1739 node.dependencies.put(rule, dependency);
1740 }
1741 }
1742 }
1743
Romain Guybc5d8762012-01-06 16:40:49 -08001744 final ArrayDeque<Node> roots = mRoots;
Romain Guy725015a2009-06-23 14:27:34 -07001745 roots.clear();
1746
1747 // Finds all the roots in the graph: all nodes with no dependencies
1748 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001749 final Node node = nodes.get(i);
Romain Guybc5d8762012-01-06 16:40:49 -08001750 if (node.dependencies.size() == 0) roots.addLast(node);
Romain Guy725015a2009-06-23 14:27:34 -07001751 }
1752
1753 return roots;
1754 }
1755
1756 /**
1757 * Prints the dependency graph for the specified rules.
1758 *
1759 * @param resources The context's resources to print the ids.
1760 * @param rules The list of rules to take into account.
1761 */
1762 void log(Resources resources, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001763 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001764 for (Node node : roots) {
1765 printNode(resources, node);
1766 }
1767 }
1768
Romain Guy9fffa1e2009-06-24 12:42:43 -07001769 static void printViewId(Resources resources, View view) {
Romain Guy725015a2009-06-23 14:27:34 -07001770 if (view.getId() != View.NO_ID) {
1771 d(LOG_TAG, resources.getResourceEntryName(view.getId()));
1772 } else {
1773 d(LOG_TAG, "NO_ID");
1774 }
1775 }
1776
1777 private static void appendViewId(Resources resources, Node node, StringBuilder buffer) {
1778 if (node.view.getId() != View.NO_ID) {
1779 buffer.append(resources.getResourceEntryName(node.view.getId()));
1780 } else {
1781 buffer.append("NO_ID");
1782 }
1783 }
1784
1785 private static void printNode(Resources resources, Node node) {
1786 if (node.dependents.size() == 0) {
1787 printViewId(resources, node.view);
1788 } else {
Romain Guybc5d8762012-01-06 16:40:49 -08001789 for (Node dependent : node.dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001790 StringBuilder buffer = new StringBuilder();
1791 appendViewId(resources, node, buffer);
1792 printdependents(resources, dependent, buffer);
1793 }
1794 }
1795 }
1796
1797 private static void printdependents(Resources resources, Node node, StringBuilder buffer) {
1798 buffer.append(" -> ");
1799 appendViewId(resources, node, buffer);
1800
1801 if (node.dependents.size() == 0) {
1802 d(LOG_TAG, buffer.toString());
1803 } else {
Romain Guybc5d8762012-01-06 16:40:49 -08001804 for (Node dependent : node.dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001805 StringBuilder subBuffer = new StringBuilder(buffer);
1806 printdependents(resources, dependent, subBuffer);
1807 }
1808 }
1809 }
1810
1811 /**
1812 * A node in the dependency graph. A node is a view, its list of dependencies
1813 * and its list of dependents.
1814 *
1815 * A node with no dependent is considered a root of the graph.
1816 */
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001817 static class Node {
Romain Guy725015a2009-06-23 14:27:34 -07001818 /**
1819 * The view representing this node in the layout.
1820 */
1821 View view;
1822
1823 /**
1824 * The list of dependents for this node; a dependent is a node
1825 * that needs this node to be processed first.
1826 */
Romain Guybc5d8762012-01-06 16:40:49 -08001827 final HashMap<Node, DependencyGraph> dependents = new HashMap<Node, DependencyGraph>();
Romain Guy725015a2009-06-23 14:27:34 -07001828
1829 /**
1830 * The list of dependencies for this node.
1831 */
1832 final SparseArray<Node> dependencies = new SparseArray<Node>();
1833
1834 /*
1835 * START POOL IMPLEMENTATION
1836 */
Romain Guybaac4632009-06-29 14:28:29 -07001837 // The pool is static, so all nodes instances are shared across
1838 // activities, that's why we give it a rather high limit
1839 private static final int POOL_LIMIT = 100;
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001840 private static final SimplePool<Node> sPool = new SimplePool<Node>(POOL_LIMIT);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001841
Romain Guy725015a2009-06-23 14:27:34 -07001842 static Node acquire(View view) {
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001843 Node node = sPool.acquire();
1844 if (node == null) {
1845 node = new Node();
1846 }
Romain Guy725015a2009-06-23 14:27:34 -07001847 node.view = view;
Romain Guy725015a2009-06-23 14:27:34 -07001848 return node;
1849 }
1850
1851 void release() {
1852 view = null;
1853 dependents.clear();
1854 dependencies.clear();
1855
1856 sPool.release(this);
1857 }
1858 /*
1859 * END POOL IMPLEMENTATION
1860 */
1861 }
1862 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863}