blob: f940226a1a60f57802b0c98a07ef368b7bd95faa [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
Romain Guy42460ac2010-01-11 16:46:33 -0800397 // TODO: we need to find another way to implement RelativeLayout
398 // This implementation cannot handle every case
Romain Guy725015a2009-06-23 14:27:34 -0700399 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Romain Guy725015a2009-06-23 14:27:34 -0700401 if (mDirtyHierarchy) {
402 mDirtyHierarchy = false;
403 sortChildren();
404 }
405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 int myWidth = -1;
407 int myHeight = -1;
408
409 int width = 0;
410 int height = 0;
411
Adam Powell132a7422012-09-30 12:39:56 -0700412 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
413 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
414 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
415 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416
417 // Record our dimensions if they are known;
418 if (widthMode != MeasureSpec.UNSPECIFIED) {
419 myWidth = widthSize;
420 }
421
422 if (heightMode != MeasureSpec.UNSPECIFIED) {
423 myHeight = heightSize;
424 }
425
426 if (widthMode == MeasureSpec.EXACTLY) {
427 width = myWidth;
428 }
429
430 if (heightMode == MeasureSpec.EXACTLY) {
431 height = myHeight;
432 }
433
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 mHasBaselineAlignedChild = false;
435
436 View ignore = null;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700437 int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700438 final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
440 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
441
442 int left = Integer.MAX_VALUE;
443 int top = Integer.MAX_VALUE;
444 int right = Integer.MIN_VALUE;
445 int bottom = Integer.MIN_VALUE;
446
Romain Guyf7dabb02009-06-25 14:47:14 -0700447 boolean offsetHorizontalAxis = false;
448 boolean offsetVerticalAxis = false;
449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
451 ignore = findViewById(mIgnoreGravity);
452 }
453
Romain Guyf7dabb02009-06-25 14:47:14 -0700454 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
455 final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
456
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800457 // We need to know our size for doing the correct computation of children positioning in RTL
458 // mode but there is no practical way to get it instead of running the code below.
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800459 // 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 -0800460 // before the computation and then, as a last pass, we will update their real position with
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800461 // an offset equals to "DEFAULT_WIDTH - width".
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800462 final int layoutDirection = getLayoutDirection();
463 if (isLayoutRtl() && myWidth == -1) {
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800464 myWidth = DEFAULT_WIDTH;
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800465 }
466
Romain Guye24ef602009-06-25 13:01:55 -0700467 View[] views = mSortedHorizontalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700468 int count = views.length;
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800469
Romain Guy725015a2009-06-23 14:27:34 -0700470 for (int i = 0; i < count; i++) {
471 View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 if (child.getVisibility() != GONE) {
473 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800474 int[] rules = params.getRules(layoutDirection);
Romain Guy95607032009-06-24 14:37:03 -0700475
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800476 applyHorizontalSizeRules(params, myWidth, rules);
Romain Guyf782e602009-06-25 15:26:49 -0700477 measureChildHorizontal(child, params, myWidth, myHeight);
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800478
Romain Guyf7dabb02009-06-25 14:47:14 -0700479 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
480 offsetHorizontalAxis = true;
481 }
Romain Guy725015a2009-06-23 14:27:34 -0700482 }
483 }
484
Romain Guye24ef602009-06-25 13:01:55 -0700485 views = mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700486 count = views.length;
Romain Guyf7dabb02009-06-25 14:47:14 -0700487
Romain Guy725015a2009-06-23 14:27:34 -0700488 for (int i = 0; i < count; i++) {
489 View child = views[i];
490 if (child.getVisibility() != GONE) {
491 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guy95607032009-06-24 14:37:03 -0700492
493 applyVerticalSizeRules(params, myHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 measureChild(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700495 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
496 offsetVerticalAxis = true;
497 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498
Romain Guyf7dabb02009-06-25 14:47:14 -0700499 if (isWrapContentWidth) {
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800500 if (isLayoutRtl()) {
501 width = Math.max(width, myWidth - params.mLeft);
502 } else {
503 width = Math.max(width, params.mRight);
504 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700506
507 if (isWrapContentHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 height = Math.max(height, params.mBottom);
509 }
510
511 if (child != ignore || verticalGravity) {
512 left = Math.min(left, params.mLeft - params.leftMargin);
513 top = Math.min(top, params.mTop - params.topMargin);
514 }
515
516 if (child != ignore || horizontalGravity) {
517 right = Math.max(right, params.mRight + params.rightMargin);
518 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
519 }
520 }
521 }
522
523 if (mHasBaselineAlignedChild) {
Romain Guy725015a2009-06-23 14:27:34 -0700524 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 View child = getChildAt(i);
526 if (child.getVisibility() != GONE) {
527 LayoutParams params = (LayoutParams) child.getLayoutParams();
528 alignBaseline(child, params);
529
530 if (child != ignore || verticalGravity) {
Romain Guy725015a2009-06-23 14:27:34 -0700531 left = Math.min(left, params.mLeft - params.leftMargin);
532 top = Math.min(top, params.mTop - params.topMargin);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 }
534
535 if (child != ignore || horizontalGravity) {
536 right = Math.max(right, params.mRight + params.rightMargin);
537 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
538 }
539 }
540 }
541 }
542
Romain Guyf7dabb02009-06-25 14:47:14 -0700543 if (isWrapContentWidth) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700544 // Width already has left padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 // the right of each child view
546 width += mPaddingRight;
547
548 if (mLayoutParams.width >= 0) {
549 width = Math.max(width, mLayoutParams.width);
550 }
551
552 width = Math.max(width, getSuggestedMinimumWidth());
553 width = resolveSize(width, widthMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700554
555 if (offsetHorizontalAxis) {
Romain Guyd10a5762009-07-28 11:18:14 -0700556 for (int i = 0; i < count; i++) {
Romain Guyf7dabb02009-06-25 14:47:14 -0700557 View child = getChildAt(i);
558 if (child.getVisibility() != GONE) {
559 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700560 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700561 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
562 centerHorizontal(child, params, width);
Romain Guy42460ac2010-01-11 16:46:33 -0800563 } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
564 final int childWidth = child.getMeasuredWidth();
565 params.mLeft = width - mPaddingRight - childWidth;
566 params.mRight = params.mLeft + childWidth;
Romain Guyf7dabb02009-06-25 14:47:14 -0700567 }
568 }
569 }
570 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700572
573 if (isWrapContentHeight) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700574 // Height already has top padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 // the bottom of each child view
576 height += mPaddingBottom;
577
578 if (mLayoutParams.height >= 0) {
579 height = Math.max(height, mLayoutParams.height);
580 }
581
582 height = Math.max(height, getSuggestedMinimumHeight());
583 height = resolveSize(height, heightMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700584
585 if (offsetVerticalAxis) {
586 for (int i = 0; i < count; i++) {
587 View child = getChildAt(i);
588 if (child.getVisibility() != GONE) {
589 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700590 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700591 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
592 centerVertical(child, params, height);
Romain Guy42460ac2010-01-11 16:46:33 -0800593 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
594 final int childHeight = child.getMeasuredHeight();
595 params.mTop = height - mPaddingBottom - childHeight;
596 params.mBottom = params.mTop + childHeight;
Romain Guyf7dabb02009-06-25 14:47:14 -0700597 }
598 }
599 }
600 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 }
602
603 if (horizontalGravity || verticalGravity) {
604 final Rect selfBounds = mSelfBounds;
605 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
606 height - mPaddingBottom);
607
608 final Rect contentBounds = mContentBounds;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700609 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
Fabrice Di Meglioc0053222011-06-13 12:16:51 -0700610 layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611
612 final int horizontalOffset = contentBounds.left - left;
613 final int verticalOffset = contentBounds.top - top;
614 if (horizontalOffset != 0 || verticalOffset != 0) {
Romain Guy725015a2009-06-23 14:27:34 -0700615 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 View child = getChildAt(i);
617 if (child.getVisibility() != GONE && child != ignore) {
618 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guyd10a5762009-07-28 11:18:14 -0700619 if (horizontalGravity) {
620 params.mLeft += horizontalOffset;
621 params.mRight += horizontalOffset;
622 }
623 if (verticalGravity) {
624 params.mTop += verticalOffset;
625 params.mBottom += verticalOffset;
626 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 }
628 }
629 }
630 }
631
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800632 if (isLayoutRtl()) {
633 final int offsetWidth = myWidth - width;
634 for (int i = 0; i < count; i++) {
635 View child = getChildAt(i);
636 if (child.getVisibility() != GONE) {
637 LayoutParams params = (LayoutParams) child.getLayoutParams();
638 params.mLeft -= offsetWidth;
639 params.mRight -= offsetWidth;
640 }
641 }
642
643 }
644
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 setMeasuredDimension(width, height);
646 }
647
648 private void alignBaseline(View child, LayoutParams params) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700649 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700650 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE);
652
653 if (anchorBaseline != -1) {
654 LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE);
655 if (anchorParams != null) {
656 int offset = anchorParams.mTop + anchorBaseline;
657 int baseline = child.getBaseline();
658 if (baseline != -1) {
659 offset -= baseline;
660 }
661 int height = params.mBottom - params.mTop;
662 params.mTop = offset;
663 params.mBottom = params.mTop + height;
664 }
665 }
666
667 if (mBaselineView == null) {
668 mBaselineView = child;
669 } else {
670 LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams();
671 if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) {
672 mBaselineView = child;
673 }
674 }
675 }
676
677 /**
678 * Measure a child. The child should have left, top, right and bottom information
679 * stored in its LayoutParams. If any of these values is -1 it means that the view
680 * can extend up to the corresponding edge.
681 *
682 * @param child Child to measure
683 * @param params LayoutParams associated with child
684 * @param myWidth Width of the the RelativeLayout
685 * @param myHeight Height of the RelativeLayout
686 */
Romain Guy725015a2009-06-23 14:27:34 -0700687 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
689 params.mRight, params.width,
690 params.leftMargin, params.rightMargin,
691 mPaddingLeft, mPaddingRight,
692 myWidth);
693 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
694 params.mBottom, params.height,
695 params.topMargin, params.bottomMargin,
696 mPaddingTop, mPaddingBottom,
697 myHeight);
698 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
699 }
700
Romain Guyf782e602009-06-25 15:26:49 -0700701 private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
Romain Guy95607032009-06-24 14:37:03 -0700702 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
703 params.mRight, params.width,
704 params.leftMargin, params.rightMargin,
705 mPaddingLeft, mPaddingRight,
706 myWidth);
Romain Guy76d59a32013-04-08 10:51:35 -0700707 int maxHeight = myHeight;
708 if (mMeasureVerticalWithPaddingMargin) {
709 maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom -
710 params.topMargin - params.bottomMargin);
711 }
Romain Guyf782e602009-06-25 15:26:49 -0700712 int childHeightMeasureSpec;
Adam Powell7da4b732012-12-07 15:28:33 -0800713 if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
Romain Guyf16c7a92013-02-11 17:43:59 -0800714 if (params.height >= 0) {
715 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
716 params.height, MeasureSpec.EXACTLY);
717 } else {
718 // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement
719 // is code for, "we got an unspecified mode in the RelativeLayout's measurespec."
720 // Carry it forward.
721 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
722 }
Adam Powell132a7422012-09-30 12:39:56 -0700723 } else if (params.width == LayoutParams.MATCH_PARENT) {
Romain Guy76d59a32013-04-08 10:51:35 -0700724 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
Romain Guyf782e602009-06-25 15:26:49 -0700725 } else {
Romain Guy76d59a32013-04-08 10:51:35 -0700726 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
Romain Guyf782e602009-06-25 15:26:49 -0700727 }
Romain Guy725015a2009-06-23 14:27:34 -0700728 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
729 }
730
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 /**
732 * Get a measure spec that accounts for all of the constraints on this view.
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700733 * This includes size constraints imposed by the RelativeLayout as well as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 * the View's desired dimension.
735 *
736 * @param childStart The left or top field of the child's layout params
737 * @param childEnd The right or bottom field of the child's layout params
738 * @param childSize The child's desired size (the width or height field of
739 * the child's layout params)
740 * @param startMargin The left or top margin
741 * @param endMargin The right or bottom margin
742 * @param startPadding mPaddingLeft or mPaddingTop
743 * @param endPadding mPaddingRight or mPaddingBottom
744 * @param mySize The width or height of this view (the RelativeLayout)
745 * @return MeasureSpec for the child
746 */
747 private int getChildMeasureSpec(int childStart, int childEnd,
748 int childSize, int startMargin, int endMargin, int startPadding,
749 int endPadding, int mySize) {
Adam Powell7da4b732012-12-07 15:28:33 -0800750 if (mySize < 0 && !mAllowBrokenMeasureSpecs) {
Romain Guyf16c7a92013-02-11 17:43:59 -0800751 if (childSize >= 0) {
752 return MeasureSpec.makeMeasureSpec(childSize, MeasureSpec.EXACTLY);
753 }
Adam Powell132a7422012-09-30 12:39:56 -0700754 // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement
755 // is code for, "we got an unspecified mode in the RelativeLayout's measurespec."
756 // Carry it forward.
757 return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
758 }
759
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 int childSpecMode = 0;
761 int childSpecSize = 0;
762
763 // Figure out start and end bounds.
764 int tempStart = childStart;
765 int tempEnd = childEnd;
766
767 // If the view did not express a layout constraint for an edge, use
768 // view's margins and our padding
769 if (tempStart < 0) {
770 tempStart = startPadding + startMargin;
771 }
772 if (tempEnd < 0) {
773 tempEnd = mySize - endPadding - endMargin;
774 }
775
776 // Figure out maximum size available to this view
777 int maxAvailable = tempEnd - tempStart;
778
779 if (childStart >= 0 && childEnd >= 0) {
780 // Constraints fixed both edges, so child must be an exact size
781 childSpecMode = MeasureSpec.EXACTLY;
782 childSpecSize = maxAvailable;
783 } else {
784 if (childSize >= 0) {
785 // Child wanted an exact size. Give as much as possible
786 childSpecMode = MeasureSpec.EXACTLY;
787
788 if (maxAvailable >= 0) {
789 // We have a maxmum size in this dimension.
790 childSpecSize = Math.min(maxAvailable, childSize);
791 } else {
792 // We can grow in this dimension.
793 childSpecSize = childSize;
794 }
Romain Guy980a9382010-01-08 15:06:28 -0800795 } else if (childSize == LayoutParams.MATCH_PARENT) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700796 // Child wanted to be as big as possible. Give all available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 // space
798 childSpecMode = MeasureSpec.EXACTLY;
799 childSpecSize = maxAvailable;
800 } else if (childSize == LayoutParams.WRAP_CONTENT) {
801 // Child wants to wrap content. Use AT_MOST
802 // to communicate available space if we know
803 // our max size
804 if (maxAvailable >= 0) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700805 // We have a maximum size in this dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 childSpecMode = MeasureSpec.AT_MOST;
807 childSpecSize = maxAvailable;
808 } else {
809 // We can grow in this dimension. Child can be as big as it
810 // wants
811 childSpecMode = MeasureSpec.UNSPECIFIED;
812 childSpecSize = 0;
813 }
814 }
815 }
816
817 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
818 }
819
Romain Guyf7dabb02009-06-25 14:47:14 -0700820 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
821 boolean wrapContent) {
822
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700823 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700824 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825
826 if (params.mLeft < 0 && params.mRight >= 0) {
827 // Right is fixed, but left varies
828 params.mLeft = params.mRight - child.getMeasuredWidth();
829 } else if (params.mLeft >= 0 && params.mRight < 0) {
830 // Left is fixed, but right varies
831 params.mRight = params.mLeft + child.getMeasuredWidth();
832 } else if (params.mLeft < 0 && params.mRight < 0) {
833 // Both left and right vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700834 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
835 if (!wrapContent) {
836 centerHorizontal(child, params, myWidth);
837 } else {
838 params.mLeft = mPaddingLeft + params.leftMargin;
839 params.mRight = params.mLeft + child.getMeasuredWidth();
840 }
841 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 } else {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700843 // This is the default case. For RTL we start from the right and for LTR we start
844 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
845 if (isLayoutRtl()) {
846 params.mRight = myWidth - mPaddingRight- params.rightMargin;
847 params.mLeft = params.mRight - child.getMeasuredWidth();
848 } else {
849 params.mLeft = mPaddingLeft + params.leftMargin;
850 params.mRight = params.mLeft + child.getMeasuredWidth();
851 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 }
853 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700854 return rules[ALIGN_PARENT_END] != 0;
Romain Guy725015a2009-06-23 14:27:34 -0700855 }
856
Romain Guyf7dabb02009-06-25 14:47:14 -0700857 private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
858 boolean wrapContent) {
859
Romain Guy725015a2009-06-23 14:27:34 -0700860 int[] rules = params.getRules();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861
862 if (params.mTop < 0 && params.mBottom >= 0) {
863 // Bottom is fixed, but top varies
864 params.mTop = params.mBottom - child.getMeasuredHeight();
865 } else if (params.mTop >= 0 && params.mBottom < 0) {
866 // Top is fixed, but bottom varies
867 params.mBottom = params.mTop + child.getMeasuredHeight();
868 } else if (params.mTop < 0 && params.mBottom < 0) {
869 // Both top and bottom vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700870 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
871 if (!wrapContent) {
872 centerVertical(child, params, myHeight);
873 } else {
874 params.mTop = mPaddingTop + params.topMargin;
875 params.mBottom = params.mTop + child.getMeasuredHeight();
876 }
877 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 } else {
879 params.mTop = mPaddingTop + params.topMargin;
880 params.mBottom = params.mTop + child.getMeasuredHeight();
881 }
882 }
Romain Guy42460ac2010-01-11 16:46:33 -0800883 return rules[ALIGN_PARENT_BOTTOM] != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 }
885
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800886 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 RelativeLayout.LayoutParams anchorParams;
888
889 // -1 indicated a "soft requirement" in that direction. For example:
890 // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right
891 // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left
892 // left=10, right=20 means the left and right ends are both fixed
893 childParams.mLeft = -1;
894 childParams.mRight = -1;
895
896 anchorParams = getRelatedViewParams(rules, LEFT_OF);
897 if (anchorParams != null) {
898 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
899 childParams.rightMargin);
900 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
901 if (myWidth >= 0) {
902 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
903 } else {
904 // FIXME uh oh...
905 }
906 }
907
908 anchorParams = getRelatedViewParams(rules, RIGHT_OF);
909 if (anchorParams != null) {
910 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
911 childParams.leftMargin);
912 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
913 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
914 }
915
916 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
917 if (anchorParams != null) {
918 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
919 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
920 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
921 }
922
923 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
924 if (anchorParams != null) {
925 childParams.mRight = anchorParams.mRight - childParams.rightMargin;
926 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
927 if (myWidth >= 0) {
928 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
929 } else {
930 // FIXME uh oh...
931 }
932 }
933
934 if (0 != rules[ALIGN_PARENT_LEFT]) {
935 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
936 }
937
938 if (0 != rules[ALIGN_PARENT_RIGHT]) {
939 if (myWidth >= 0) {
940 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
941 } else {
942 // FIXME uh oh...
943 }
944 }
Romain Guy725015a2009-06-23 14:27:34 -0700945 }
946
947 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) {
948 int[] rules = childParams.getRules();
949 RelativeLayout.LayoutParams anchorParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950
951 childParams.mTop = -1;
952 childParams.mBottom = -1;
953
954 anchorParams = getRelatedViewParams(rules, ABOVE);
955 if (anchorParams != null) {
956 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
957 childParams.bottomMargin);
958 } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
959 if (myHeight >= 0) {
960 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
961 } else {
962 // FIXME uh oh...
963 }
964 }
965
966 anchorParams = getRelatedViewParams(rules, BELOW);
967 if (anchorParams != null) {
968 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
969 childParams.topMargin);
970 } else if (childParams.alignWithParent && rules[BELOW] != 0) {
971 childParams.mTop = mPaddingTop + childParams.topMargin;
972 }
973
974 anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
975 if (anchorParams != null) {
976 childParams.mTop = anchorParams.mTop + childParams.topMargin;
977 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
978 childParams.mTop = mPaddingTop + childParams.topMargin;
979 }
980
981 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
982 if (anchorParams != null) {
983 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
984 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
985 if (myHeight >= 0) {
986 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
987 } else {
988 // FIXME uh oh...
989 }
990 }
991
992 if (0 != rules[ALIGN_PARENT_TOP]) {
993 childParams.mTop = mPaddingTop + childParams.topMargin;
994 }
995
996 if (0 != rules[ALIGN_PARENT_BOTTOM]) {
997 if (myHeight >= 0) {
998 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
999 } else {
1000 // FIXME uh oh...
1001 }
1002 }
1003
1004 if (rules[ALIGN_BASELINE] != 0) {
1005 mHasBaselineAlignedChild = true;
1006 }
1007 }
1008
1009 private View getRelatedView(int[] rules, int relation) {
1010 int id = rules[relation];
1011 if (id != 0) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001012 DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
Romain Guya0fd1d72009-06-24 14:25:43 -07001013 if (node == null) return null;
1014 View v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015
1016 // Find the first non-GONE view up the chain
1017 while (v.getVisibility() == View.GONE) {
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001018 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
Romain Guy1ab621e2009-06-25 13:31:57 -07001019 node = mGraph.mKeyNodes.get((rules[relation]));
Romain Guya0fd1d72009-06-24 14:25:43 -07001020 if (node == null) return null;
1021 v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 }
1023
1024 return v;
1025 }
1026
1027 return null;
1028 }
1029
1030 private LayoutParams getRelatedViewParams(int[] rules, int relation) {
1031 View v = getRelatedView(rules, relation);
1032 if (v != null) {
1033 ViewGroup.LayoutParams params = v.getLayoutParams();
1034 if (params instanceof LayoutParams) {
1035 return (LayoutParams) v.getLayoutParams();
1036 }
1037 }
1038 return null;
1039 }
1040
1041 private int getRelatedViewBaseline(int[] rules, int relation) {
1042 View v = getRelatedView(rules, relation);
1043 if (v != null) {
1044 return v.getBaseline();
1045 }
1046 return -1;
1047 }
1048
Romain Guyf16c7a92013-02-11 17:43:59 -08001049 private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 int childWidth = child.getMeasuredWidth();
1051 int left = (myWidth - childWidth) / 2;
1052
1053 params.mLeft = left;
1054 params.mRight = left + childWidth;
1055 }
1056
Romain Guyf16c7a92013-02-11 17:43:59 -08001057 private static void centerVertical(View child, LayoutParams params, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 int childHeight = child.getMeasuredHeight();
1059 int top = (myHeight - childHeight) / 2;
1060
1061 params.mTop = top;
1062 params.mBottom = top + childHeight;
1063 }
1064
1065 @Override
1066 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1067 // The layout has actually already been performed and the positions
1068 // cached. Apply the cached values to the children.
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001069 final int count = getChildCount();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070
1071 for (int i = 0; i < count; i++) {
1072 View child = getChildAt(i);
1073 if (child.getVisibility() != GONE) {
1074 RelativeLayout.LayoutParams st =
1075 (RelativeLayout.LayoutParams) child.getLayoutParams();
1076 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
1078 }
1079 }
1080
1081 @Override
1082 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1083 return new RelativeLayout.LayoutParams(getContext(), attrs);
1084 }
1085
1086 /**
1087 * Returns a set of layout parameters with a width of
1088 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1089 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1090 */
1091 @Override
1092 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1093 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1094 }
1095
1096 // Override to allow type-checking of LayoutParams.
1097 @Override
1098 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1099 return p instanceof RelativeLayout.LayoutParams;
1100 }
1101
1102 @Override
1103 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1104 return new LayoutParams(p);
1105 }
1106
svetoslavganov75986cf2009-05-14 22:28:01 -07001107 @Override
1108 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1109 if (mTopToBottomLeftToRightSet == null) {
1110 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1111 }
1112
1113 // sort children top-to-bottom and left-to-right
1114 for (int i = 0, count = getChildCount(); i < count; i++) {
1115 mTopToBottomLeftToRightSet.add(getChildAt(i));
1116 }
1117
1118 for (View view : mTopToBottomLeftToRightSet) {
Svetoslav Ganov0b0a41d2011-09-07 18:06:03 -07001119 if (view.getVisibility() == View.VISIBLE
1120 && view.dispatchPopulateAccessibilityEvent(event)) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001121 mTopToBottomLeftToRightSet.clear();
1122 return true;
1123 }
1124 }
1125
1126 mTopToBottomLeftToRightSet.clear();
1127 return false;
1128 }
1129
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001130 @Override
1131 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1132 super.onInitializeAccessibilityEvent(event);
1133 event.setClassName(RelativeLayout.class.getName());
1134 }
1135
1136 @Override
1137 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1138 super.onInitializeAccessibilityNodeInfo(info);
1139 info.setClassName(RelativeLayout.class.getName());
1140 }
1141
svetoslavganov75986cf2009-05-14 22:28:01 -07001142 /**
1143 * Compares two views in left-to-right and top-to-bottom fashion.
1144 */
1145 private class TopToBottomLeftToRightComparator implements Comparator<View> {
1146 public int compare(View first, View second) {
1147 // top - bottom
1148 int topDifference = first.getTop() - second.getTop();
1149 if (topDifference != 0) {
1150 return topDifference;
1151 }
1152 // left - right
1153 int leftDifference = first.getLeft() - second.getLeft();
1154 if (leftDifference != 0) {
1155 return leftDifference;
1156 }
1157 // break tie by height
1158 int heightDiference = first.getHeight() - second.getHeight();
1159 if (heightDiference != 0) {
1160 return heightDiference;
1161 }
1162 // break tie by width
1163 int widthDiference = first.getWidth() - second.getWidth();
1164 if (widthDiference != 0) {
1165 return widthDiference;
1166 }
1167 return 0;
1168 }
1169 }
1170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 /**
1172 * Per-child layout information associated with RelativeLayout.
1173 *
1174 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1175 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1176 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1177 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1178 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1179 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1180 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1181 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1182 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1183 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1184 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1185 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1186 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1187 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1188 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1189 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1190 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001191 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1192 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1193 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1194 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1195 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1196 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 */
1198 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001199 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001200 @ViewDebug.IntToString(from = ABOVE, to = "above"),
1201 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
1202 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
1203 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"),
1204 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1205 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"),
1206 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"),
1207 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"),
1208 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"),
1209 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"),
1210 @ViewDebug.IntToString(from = BELOW, to = "below"),
1211 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"),
1212 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"),
1213 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"),
1214 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"),
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001215 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"),
1216 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"),
1217 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"),
1218 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"),
1219 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"),
1220 @ViewDebug.IntToString(from = START_OF, to = "startOf"),
1221 @ViewDebug.IntToString(from = END_OF, to = "endOf")
The Android Open Source Project10592532009-03-18 17:39:46 -07001222 }, mapping = {
1223 @ViewDebug.IntToString(from = TRUE, to = "true"),
Romain Guya1f3e4a2009-06-04 15:10:46 -07001224 @ViewDebug.IntToString(from = 0, to = "false/NO_ID")
The Android Open Source Project10592532009-03-18 17:39:46 -07001225 })
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001226
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001227 private int[] mRules = new int[VERB_COUNT];
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001228 private int[] mInitialRules = new int[VERB_COUNT];
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 private int mLeft, mTop, mRight, mBottom;
1231
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001232 private int mStart = DEFAULT_MARGIN_RELATIVE;
1233 private int mEnd = DEFAULT_MARGIN_RELATIVE;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001234
1235 private boolean mRulesChanged = false;
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001236 private boolean mIsRtlCompatibilityMode = false;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 /**
1239 * When true, uses the parent as the anchor if the anchor doesn't exist or if
1240 * the anchor's visibility is GONE.
1241 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001242 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 public boolean alignWithParent;
1244
1245 public LayoutParams(Context c, AttributeSet attrs) {
1246 super(c, attrs);
1247
1248 TypedArray a = c.obtainStyledAttributes(attrs,
1249 com.android.internal.R.styleable.RelativeLayout_Layout);
1250
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001251 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
1252 mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
1253 !c.getApplicationInfo().hasRtlSupport());
1254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 final int[] rules = mRules;
Romain Guyf16c7a92013-02-11 17:43:59 -08001256 //noinspection MismatchedReadAndWriteOfArray
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001257 final int[] initialRules = mInitialRules;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258
1259 final int N = a.getIndexCount();
1260 for (int i = 0; i < N; i++) {
1261 int attr = a.getIndex(i);
1262 switch (attr) {
1263 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1264 alignWithParent = a.getBoolean(attr, false);
1265 break;
1266 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1267 rules[LEFT_OF] = a.getResourceId(attr, 0);
1268 break;
1269 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1270 rules[RIGHT_OF] = a.getResourceId(attr, 0);
1271 break;
1272 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1273 rules[ABOVE] = a.getResourceId(attr, 0);
1274 break;
1275 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1276 rules[BELOW] = a.getResourceId(attr, 0);
1277 break;
1278 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1279 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1280 break;
1281 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1282 rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1283 break;
1284 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1285 rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1286 break;
1287 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1288 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1289 break;
1290 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1291 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1292 break;
1293 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1294 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1295 break;
1296 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1297 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1298 break;
1299 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1300 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1301 break;
1302 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1303 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1304 break;
1305 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1306 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1307 break;
1308 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1309 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1310 break;
1311 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1312 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1313 break;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001314 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1315 rules[START_OF] = a.getResourceId(attr, 0);
1316 break;
1317 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1318 rules[END_OF] = a.getResourceId(attr, 0);
1319 break;
1320 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1321 rules[ALIGN_START] = a.getResourceId(attr, 0);
1322 break;
1323 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1324 rules[ALIGN_END] = a.getResourceId(attr, 0);
1325 break;
1326 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1327 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1328 break;
1329 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1330 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1331 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 }
1333 }
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001334 mRulesChanged = true;
Romain Guyf16c7a92013-02-11 17:43:59 -08001335 System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 a.recycle();
1338 }
1339
1340 public LayoutParams(int w, int h) {
1341 super(w, h);
1342 }
1343
1344 /**
1345 * {@inheritDoc}
1346 */
1347 public LayoutParams(ViewGroup.LayoutParams source) {
1348 super(source);
1349 }
1350
1351 /**
1352 * {@inheritDoc}
1353 */
1354 public LayoutParams(ViewGroup.MarginLayoutParams source) {
1355 super(source);
1356 }
1357
1358 @Override
1359 public String debug(String output) {
1360 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1361 ", height=" + sizeToString(height) + " }";
1362 }
1363
1364 /**
1365 * Adds a layout rule to be interpreted by the RelativeLayout. This
1366 * method should only be used for constraints that don't refer to another sibling
1367 * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE}
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001368 * 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 -08001369 * {@link #addRule(int, int)} instead.
1370 *
1371 * @param verb One of the verbs defined by
1372 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1373 * ALIGN_WITH_PARENT_LEFT.
1374 * @see #addRule(int, int)
1375 */
1376 public void addRule(int verb) {
1377 mRules[verb] = TRUE;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001378 mInitialRules[verb] = TRUE;
1379 mRulesChanged = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 }
1381
1382 /**
1383 * Adds a layout rule to be interpreted by the RelativeLayout. Use this for
1384 * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
1385 * value (VISIBLE).
1386 *
1387 * @param verb One of the verbs defined by
1388 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1389 * ALIGN_WITH_PARENT_LEFT.
1390 * @param anchor The id of another view to use as an anchor,
1391 * or a boolean value(represented as {@link RelativeLayout#TRUE})
1392 * for true or 0 for false). For verbs that don't refer to another sibling
1393 * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
1394 * @see #addRule(int)
1395 */
1396 public void addRule(int verb, int anchor) {
1397 mRules[verb] = anchor;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001398 mInitialRules[verb] = anchor;
1399 mRulesChanged = true;
1400 }
1401
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001402 /**
1403 * Removes a layout rule to be interpreted by the RelativeLayout.
1404 *
1405 * @param verb One of the verbs defined by
1406 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1407 * ALIGN_WITH_PARENT_LEFT.
1408 * @see #addRule(int)
1409 * @see #addRule(int, int)
1410 */
1411 public void removeRule(int verb) {
1412 mRules[verb] = 0;
1413 mInitialRules[verb] = 0;
1414 mRulesChanged = true;
1415 }
1416
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001417 private boolean hasRelativeRules() {
1418 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1419 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1420 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1421 }
1422
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001423 // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
1424 // or not.
1425 //
1426 // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
1427 // predominance over any "start/end" rules that could have been defined. A special case:
1428 // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
1429 // resolve those "start"/"end" rules to "left"/"right" respectively.
1430 //
1431 // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
1432 // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
1433 //
1434 // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
1435 // only the "left"/"right" rules at the end.
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001436 private void resolveRules(int layoutDirection) {
1437 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001438
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001439 // Reset to initial state
Romain Guyf16c7a92013-02-11 17:43:59 -08001440 System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001441
1442 // Apply rules depending on direction and if we are in RTL compatibility mode
1443 if (mIsRtlCompatibilityMode) {
1444 if (mRules[ALIGN_START] != 0) {
1445 if (mRules[ALIGN_LEFT] == 0) {
1446 // "left" rule is not defined but "start" rule is: use the "start" rule as
1447 // the "left" rule
1448 mRules[ALIGN_LEFT] = mRules[ALIGN_START];
1449 }
1450 mRules[ALIGN_START] = 0;
1451 }
1452
1453 if (mRules[ALIGN_END] != 0) {
1454 if (mRules[ALIGN_RIGHT] == 0) {
1455 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1456 // "right" rule
1457 mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
1458 }
1459 mRules[ALIGN_END] = 0;
1460 }
1461
1462 if (mRules[START_OF] != 0) {
1463 if (mRules[LEFT_OF] == 0) {
1464 // "left" rule is not defined but "start" rule is: use the "start" rule as
1465 // the "left" rule
1466 mRules[LEFT_OF] = mRules[START_OF];
1467 }
1468 mRules[START_OF] = 0;
1469 }
1470
1471 if (mRules[END_OF] != 0) {
1472 if (mRules[RIGHT_OF] == 0) {
1473 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1474 // "right" rule
1475 mRules[RIGHT_OF] = mRules[END_OF];
1476 }
1477 mRules[END_OF] = 0;
1478 }
1479
1480 if (mRules[ALIGN_PARENT_START] != 0) {
1481 if (mRules[ALIGN_PARENT_LEFT] == 0) {
1482 // "left" rule is not defined but "start" rule is: use the "start" rule as
1483 // the "left" rule
1484 mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1485 }
1486 mRules[ALIGN_PARENT_START] = 0;
1487 }
1488
1489 if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1490 if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1491 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1492 // "right" rule
1493 mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1494 }
1495 mRules[ALIGN_PARENT_END] = 0;
1496 }
1497 } else {
1498 // JB MR1+ case
1499 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
1500 (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
1501 // "start"/"end" rules take precedence over "left"/"right" rules
1502 mRules[ALIGN_LEFT] = 0;
1503 mRules[ALIGN_RIGHT] = 0;
1504 }
1505 if (mRules[ALIGN_START] != 0) {
1506 // "start" rule resolved to "left" or "right" depending on the direction
1507 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1508 mRules[ALIGN_START] = 0;
1509 }
1510 if (mRules[ALIGN_END] != 0) {
1511 // "end" rule resolved to "left" or "right" depending on the direction
1512 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1513 mRules[ALIGN_END] = 0;
1514 }
1515
1516 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
1517 (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
1518 // "start"/"end" rules take precedence over "left"/"right" rules
1519 mRules[LEFT_OF] = 0;
1520 mRules[RIGHT_OF] = 0;
1521 }
1522 if (mRules[START_OF] != 0) {
1523 // "start" rule resolved to "left" or "right" depending on the direction
1524 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1525 mRules[START_OF] = 0;
1526 }
1527 if (mRules[END_OF] != 0) {
1528 // "end" rule resolved to "left" or "right" depending on the direction
1529 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1530 mRules[END_OF] = 0;
1531 }
1532
1533 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
1534 (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
1535 // "start"/"end" rules take precedence over "left"/"right" rules
1536 mRules[ALIGN_PARENT_LEFT] = 0;
1537 mRules[ALIGN_PARENT_RIGHT] = 0;
1538 }
1539 if (mRules[ALIGN_PARENT_START] != 0) {
1540 // "start" rule resolved to "left" or "right" depending on the direction
1541 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1542 mRules[ALIGN_PARENT_START] = 0;
1543 }
1544 if (mRules[ALIGN_PARENT_END] != 0) {
1545 // "end" rule resolved to "left" or "right" depending on the direction
1546 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1547 mRules[ALIGN_PARENT_END] = 0;
1548 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001549 }
1550 mRulesChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 }
1552
1553 /**
1554 * Retrieves a complete list of all supported rules, where the index is the rule
1555 * verb, and the element value is the value specified, or "false" if it was never
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001556 * set. If there are relative rules defined (*_START / *_END), they will be resolved
1557 * depending on the layout direction.
1558 *
1559 * @param layoutDirection the direction of the layout.
1560 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
1561 * or {@link View#LAYOUT_DIRECTION_RTL}
1562 * @return the supported rules
1563 * @see #addRule(int, int)
1564 *
1565 * @hide
1566 */
1567 public int[] getRules(int layoutDirection) {
1568 if (hasRelativeRules() &&
1569 (mRulesChanged || layoutDirection != getLayoutDirection())) {
1570 resolveRules(layoutDirection);
1571 if (layoutDirection != getLayoutDirection()) {
1572 setLayoutDirection(layoutDirection);
1573 }
1574 }
1575 return mRules;
1576 }
1577
1578 /**
1579 * Retrieves a complete list of all supported rules, where the index is the rule
1580 * verb, and the element value is the value specified, or "false" if it was never
1581 * set. There will be no resolution of relative rules done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001582 *
1583 * @return the supported rules
1584 * @see #addRule(int, int)
1585 */
1586 public int[] getRules() {
1587 return mRules;
1588 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001589
1590 @Override
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001591 public void resolveLayoutDirection(int layoutDirection) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001592 final boolean isLayoutRtl = isLayoutRtl();
1593 if (isLayoutRtl) {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001594 if (mStart != DEFAULT_MARGIN_RELATIVE) mRight = mStart;
1595 if (mEnd != DEFAULT_MARGIN_RELATIVE) mLeft = mEnd;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001596 } else {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001597 if (mStart != DEFAULT_MARGIN_RELATIVE) mLeft = mStart;
1598 if (mEnd != DEFAULT_MARGIN_RELATIVE) mRight = mEnd;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001599 }
1600
1601 if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
1602 resolveRules(layoutDirection);
1603 }
1604 // This will set the layout direction
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001605 super.resolveLayoutDirection(layoutDirection);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001606 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607 }
Romain Guy725015a2009-06-23 14:27:34 -07001608
1609 private static class DependencyGraph {
1610 /**
Romain Guy1ab621e2009-06-25 13:31:57 -07001611 * List of all views in the graph.
1612 */
1613 private ArrayList<Node> mNodes = new ArrayList<Node>();
1614
1615 /**
Romain Guy725015a2009-06-23 14:27:34 -07001616 * List of nodes in the graph. Each node is identified by its
1617 * view id (see View#getId()).
1618 */
Romain Guy1ab621e2009-06-25 13:31:57 -07001619 private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001620
1621 /**
1622 * Temporary data structure used to build the list of roots
1623 * for this graph.
1624 */
Romain Guybc5d8762012-01-06 16:40:49 -08001625 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001626
1627 /**
1628 * Clears the graph.
1629 */
1630 void clear() {
Romain Guy1ab621e2009-06-25 13:31:57 -07001631 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001632 final int count = nodes.size();
1633
1634 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001635 nodes.get(i).release();
Romain Guy725015a2009-06-23 14:27:34 -07001636 }
1637 nodes.clear();
1638
Romain Guy1ab621e2009-06-25 13:31:57 -07001639 mKeyNodes.clear();
Romain Guy725015a2009-06-23 14:27:34 -07001640 mRoots.clear();
1641 }
1642
1643 /**
1644 * Adds a view to the graph.
1645 *
1646 * @param view The view to be added as a node to the graph.
1647 */
1648 void add(View view) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001649 final int id = view.getId();
1650 final Node node = Node.acquire(view);
1651
1652 if (id != View.NO_ID) {
1653 mKeyNodes.put(id, node);
1654 }
1655
1656 mNodes.add(node);
Romain Guy725015a2009-06-23 14:27:34 -07001657 }
1658
1659 /**
1660 * Builds a sorted list of views. The sorting order depends on the dependencies
1661 * between the view. For instance, if view C needs view A to be processed first
1662 * and view A needs view B to be processed first, the dependency graph
1663 * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1664 *
1665 * @param sorted The sorted list of views. The length of this array must
1666 * be equal to getChildCount().
1667 * @param rules The list of rules to take into account.
1668 */
1669 void getSortedViews(View[] sorted, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001670 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001671 int index = 0;
1672
Romain Guybc5d8762012-01-06 16:40:49 -08001673 Node node;
1674 while ((node = roots.pollLast()) != null) {
Romain Guy725015a2009-06-23 14:27:34 -07001675 final View view = node.view;
1676 final int key = view.getId();
1677
1678 sorted[index++] = view;
1679
Romain Guybc5d8762012-01-06 16:40:49 -08001680 final HashMap<Node, DependencyGraph> dependents = node.dependents;
1681 for (Node dependent : dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001682 final SparseArray<Node> dependencies = dependent.dependencies;
1683
1684 dependencies.remove(key);
1685 if (dependencies.size() == 0) {
1686 roots.add(dependent);
1687 }
1688 }
1689 }
1690
1691 if (index < sorted.length) {
1692 throw new IllegalStateException("Circular dependencies cannot exist"
1693 + " in RelativeLayout");
1694 }
1695 }
1696
1697 /**
1698 * Finds the roots of the graph. A root is a node with no dependency and
1699 * with [0..n] dependents.
1700 *
1701 * @param rulesFilter The list of rules to consider when building the
1702 * dependencies
1703 *
1704 * @return A list of node, each being a root of the graph
1705 */
Romain Guybc5d8762012-01-06 16:40:49 -08001706 private ArrayDeque<Node> findRoots(int[] rulesFilter) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001707 final SparseArray<Node> keyNodes = mKeyNodes;
1708 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001709 final int count = nodes.size();
1710
1711 // Find roots can be invoked several times, so make sure to clear
1712 // all dependents and dependencies before running the algorithm
1713 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001714 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001715 node.dependents.clear();
1716 node.dependencies.clear();
1717 }
1718
1719 // Builds up the dependents and dependencies for each node of the graph
1720 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001721 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001722
1723 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1724 final int[] rules = layoutParams.mRules;
1725 final int rulesCount = rulesFilter.length;
1726
1727 // Look only the the rules passed in parameter, this way we build only the
1728 // dependencies for a specific set of rules
1729 for (int j = 0; j < rulesCount; j++) {
1730 final int rule = rules[rulesFilter[j]];
1731 if (rule > 0) {
1732 // The node this node depends on
Romain Guy1ab621e2009-06-25 13:31:57 -07001733 final Node dependency = keyNodes.get(rule);
Romain Guyda3003e2009-07-19 19:47:42 -07001734 // Skip unknowns and self dependencies
1735 if (dependency == null || dependency == node) {
Romain Guyb8f8de82009-06-25 12:03:56 -07001736 continue;
1737 }
Romain Guy725015a2009-06-23 14:27:34 -07001738 // Add the current node as a dependent
Romain Guybc5d8762012-01-06 16:40:49 -08001739 dependency.dependents.put(node, this);
Romain Guy725015a2009-06-23 14:27:34 -07001740 // Add a dependency to the current node
1741 node.dependencies.put(rule, dependency);
1742 }
1743 }
1744 }
1745
Romain Guybc5d8762012-01-06 16:40:49 -08001746 final ArrayDeque<Node> roots = mRoots;
Romain Guy725015a2009-06-23 14:27:34 -07001747 roots.clear();
1748
1749 // Finds all the roots in the graph: all nodes with no dependencies
1750 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001751 final Node node = nodes.get(i);
Romain Guybc5d8762012-01-06 16:40:49 -08001752 if (node.dependencies.size() == 0) roots.addLast(node);
Romain Guy725015a2009-06-23 14:27:34 -07001753 }
1754
1755 return roots;
1756 }
1757
1758 /**
1759 * Prints the dependency graph for the specified rules.
1760 *
1761 * @param resources The context's resources to print the ids.
1762 * @param rules The list of rules to take into account.
1763 */
1764 void log(Resources resources, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001765 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001766 for (Node node : roots) {
1767 printNode(resources, node);
1768 }
1769 }
1770
Romain Guy9fffa1e2009-06-24 12:42:43 -07001771 static void printViewId(Resources resources, View view) {
Romain Guy725015a2009-06-23 14:27:34 -07001772 if (view.getId() != View.NO_ID) {
1773 d(LOG_TAG, resources.getResourceEntryName(view.getId()));
1774 } else {
1775 d(LOG_TAG, "NO_ID");
1776 }
1777 }
1778
1779 private static void appendViewId(Resources resources, Node node, StringBuilder buffer) {
1780 if (node.view.getId() != View.NO_ID) {
1781 buffer.append(resources.getResourceEntryName(node.view.getId()));
1782 } else {
1783 buffer.append("NO_ID");
1784 }
1785 }
1786
1787 private static void printNode(Resources resources, Node node) {
1788 if (node.dependents.size() == 0) {
1789 printViewId(resources, node.view);
1790 } else {
Romain Guybc5d8762012-01-06 16:40:49 -08001791 for (Node dependent : node.dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001792 StringBuilder buffer = new StringBuilder();
1793 appendViewId(resources, node, buffer);
1794 printdependents(resources, dependent, buffer);
1795 }
1796 }
1797 }
1798
1799 private static void printdependents(Resources resources, Node node, StringBuilder buffer) {
1800 buffer.append(" -> ");
1801 appendViewId(resources, node, buffer);
1802
1803 if (node.dependents.size() == 0) {
1804 d(LOG_TAG, buffer.toString());
1805 } else {
Romain Guybc5d8762012-01-06 16:40:49 -08001806 for (Node dependent : node.dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001807 StringBuilder subBuffer = new StringBuilder(buffer);
1808 printdependents(resources, dependent, subBuffer);
1809 }
1810 }
1811 }
1812
1813 /**
1814 * A node in the dependency graph. A node is a view, its list of dependencies
1815 * and its list of dependents.
1816 *
1817 * A node with no dependent is considered a root of the graph.
1818 */
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001819 static class Node {
Romain Guy725015a2009-06-23 14:27:34 -07001820 /**
1821 * The view representing this node in the layout.
1822 */
1823 View view;
1824
1825 /**
1826 * The list of dependents for this node; a dependent is a node
1827 * that needs this node to be processed first.
1828 */
Romain Guybc5d8762012-01-06 16:40:49 -08001829 final HashMap<Node, DependencyGraph> dependents = new HashMap<Node, DependencyGraph>();
Romain Guy725015a2009-06-23 14:27:34 -07001830
1831 /**
1832 * The list of dependencies for this node.
1833 */
1834 final SparseArray<Node> dependencies = new SparseArray<Node>();
1835
1836 /*
1837 * START POOL IMPLEMENTATION
1838 */
Romain Guybaac4632009-06-29 14:28:29 -07001839 // The pool is static, so all nodes instances are shared across
1840 // activities, that's why we give it a rather high limit
1841 private static final int POOL_LIMIT = 100;
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001842 private static final SimplePool<Node> sPool = new SimplePool<Node>(POOL_LIMIT);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001843
Romain Guy725015a2009-06-23 14:27:34 -07001844 static Node acquire(View view) {
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001845 Node node = sPool.acquire();
1846 if (node == null) {
1847 node = new Node();
1848 }
Romain Guy725015a2009-06-23 14:27:34 -07001849 node.view = view;
Romain Guy725015a2009-06-23 14:27:34 -07001850 return node;
1851 }
1852
1853 void release() {
1854 view = null;
1855 dependents.clear();
1856 dependencies.clear();
1857
1858 sPool.release(this);
1859 }
1860 /*
1861 * END POOL IMPLEMENTATION
1862 */
1863 }
1864 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001865}