blob: 27fda24319db0250c3ec5262ac08bebe55e12a66 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070019import com.android.internal.R;
20
Romain Guybc5d8762012-01-06 16:40:49 -080021import java.util.ArrayDeque;
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070022import java.util.ArrayList;
23import java.util.Comparator;
Romain Guybc5d8762012-01-06 16:40:49 -080024import java.util.HashMap;
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070025import java.util.SortedSet;
26import java.util.TreeSet;
27
svetoslavganov75986cf2009-05-14 22:28:01 -070028import android.content.Context;
Romain Guy725015a2009-06-23 14:27:34 -070029import android.content.res.Resources;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070030import android.content.res.TypedArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070031import android.graphics.Rect;
32import android.util.AttributeSet;
Romain Guy725015a2009-06-23 14:27:34 -070033import android.util.Pool;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070034import android.util.Poolable;
Romain Guy725015a2009-06-23 14:27:34 -070035import android.util.PoolableManager;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070036import android.util.Pools;
37import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070038import android.view.Gravity;
39import android.view.View;
40import android.view.ViewDebug;
41import android.view.ViewGroup;
42import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080043import android.view.accessibility.AccessibilityNodeInfo;
svetoslavganov75986cf2009-05-14 22:28:01 -070044import android.widget.RemoteViews.RemoteView;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070045
46import static android.util.Log.d;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48/**
49 * A Layout where the positions of the children can be described in relation to each other or to the
Romain Guy520c4202010-01-04 10:56:38 -080050 * parent.
Romain Guya1f3e4a2009-06-04 15:10:46 -070051 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 * <p>
53 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
54 * position of its children. For example, you cannot have a RelativeLayout whose height is set to
55 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
56 * {@link #ALIGN_PARENT_BOTTOM}.
57 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070058 *
Scott Main4c359b72012-07-24 15:51:27 -070059 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
60 * Layout</a> guide.</p>
Scott Main41ec6532010-08-19 16:57:07 -070061 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 * <p>
63 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
64 * layout attributes
65 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070066 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 * @attr ref android.R.styleable#RelativeLayout_gravity
68 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
69 */
70@RemoteView
71public class RelativeLayout extends ViewGroup {
Romain Guy725015a2009-06-23 14:27:34 -070072 private static final String LOG_TAG = "RelativeLayout";
73
74 private static final boolean DEBUG_GRAPH = false;
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 public static final int TRUE = -1;
77
78 /**
79 * Rule that aligns a child's right edge with another child's left edge.
80 */
81 public static final int LEFT_OF = 0;
82 /**
83 * Rule that aligns a child's left edge with another child's right edge.
84 */
85 public static final int RIGHT_OF = 1;
86 /**
87 * Rule that aligns a child's bottom edge with another child's top edge.
88 */
89 public static final int ABOVE = 2;
90 /**
91 * Rule that aligns a child's top edge with another child's bottom edge.
92 */
93 public static final int BELOW = 3;
94
95 /**
96 * Rule that aligns a child's baseline with another child's baseline.
97 */
98 public static final int ALIGN_BASELINE = 4;
99 /**
100 * Rule that aligns a child's left edge with another child's left edge.
101 */
102 public static final int ALIGN_LEFT = 5;
103 /**
104 * Rule that aligns a child's top edge with another child's top edge.
105 */
106 public static final int ALIGN_TOP = 6;
107 /**
108 * Rule that aligns a child's right edge with another child's right edge.
109 */
110 public static final int ALIGN_RIGHT = 7;
111 /**
112 * Rule that aligns a child's bottom edge with another child's bottom edge.
113 */
114 public static final int ALIGN_BOTTOM = 8;
115
116 /**
117 * Rule that aligns the child's left edge with its RelativeLayout
118 * parent's left edge.
119 */
120 public static final int ALIGN_PARENT_LEFT = 9;
121 /**
122 * Rule that aligns the child's top edge with its RelativeLayout
123 * parent's top edge.
124 */
125 public static final int ALIGN_PARENT_TOP = 10;
126 /**
127 * Rule that aligns the child's right edge with its RelativeLayout
128 * parent's right edge.
129 */
130 public static final int ALIGN_PARENT_RIGHT = 11;
131 /**
132 * Rule that aligns the child's bottom edge with its RelativeLayout
133 * parent's bottom edge.
134 */
135 public static final int ALIGN_PARENT_BOTTOM = 12;
136
137 /**
138 * Rule that centers the child with respect to the bounds of its
139 * RelativeLayout parent.
140 */
141 public static final int CENTER_IN_PARENT = 13;
142 /**
143 * Rule that centers the child horizontally with respect to the
144 * bounds of its RelativeLayout parent.
145 */
146 public static final int CENTER_HORIZONTAL = 14;
147 /**
148 * Rule that centers the child vertically with respect to the
149 * bounds of its RelativeLayout parent.
150 */
151 public static final int CENTER_VERTICAL = 15;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700152 /**
153 * Rule that aligns a child's end edge with another child's start edge.
154 */
155 public static final int START_OF = 16;
156 /**
157 * Rule that aligns a child's start edge with another child's end edge.
158 */
159 public static final int END_OF = 17;
160 /**
161 * Rule that aligns a child's start edge with another child's start edge.
162 */
163 public static final int ALIGN_START = 18;
164 /**
165 * Rule that aligns a child's end edge with another child's end edge.
166 */
167 public static final int ALIGN_END = 19;
168 /**
169 * Rule that aligns the child's start edge with its RelativeLayout
170 * parent's start edge.
171 */
172 public static final int ALIGN_PARENT_START = 20;
173 /**
174 * Rule that aligns the child's end edge with its RelativeLayout
175 * parent's end edge.
176 */
177 public static final int ALIGN_PARENT_END = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700179 private static final int VERB_COUNT = 22;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180
Romain Guybc5d8762012-01-06 16:40:49 -0800181
182 private static final int[] RULES_VERTICAL = {
183 ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
184 };
185
186 private static final int[] RULES_HORIZONTAL = {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700187 LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
Romain Guybc5d8762012-01-06 16:40:49 -0800188 };
189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 private View mBaselineView = null;
191 private boolean mHasBaselineAlignedChild;
192
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700193 private int mGravity = Gravity.START | Gravity.TOP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 private final Rect mContentBounds = new Rect();
195 private final Rect mSelfBounds = new Rect();
196 private int mIgnoreGravity;
197
Romain Guy725015a2009-06-23 14:27:34 -0700198 private SortedSet<View> mTopToBottomLeftToRightSet = null;
199
200 private boolean mDirtyHierarchy;
201 private View[] mSortedHorizontalChildren = new View[0];
202 private View[] mSortedVerticalChildren = new View[0];
203 private final DependencyGraph mGraph = new DependencyGraph();
svetoslavganov75986cf2009-05-14 22:28:01 -0700204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 public RelativeLayout(Context context) {
206 super(context);
207 }
208
209 public RelativeLayout(Context context, AttributeSet attrs) {
210 super(context, attrs);
211 initFromAttributes(context, attrs);
212 }
213
214 public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
215 super(context, attrs, defStyle);
216 initFromAttributes(context, attrs);
217 }
218
219 private void initFromAttributes(Context context, AttributeSet attrs) {
220 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout);
221 mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
222 mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
223 a.recycle();
224 }
225
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700226 @Override
227 public boolean shouldDelayChildPressedState() {
228 return false;
229 }
230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 /**
232 * Defines which View is ignored when the gravity is applied. This setting has no
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700233 * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 *
235 * @param viewId The id of the View to be ignored by gravity, or 0 if no View
236 * should be ignored.
237 *
238 * @see #setGravity(int)
239 *
240 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
241 */
242 @android.view.RemotableViewMethod
243 public void setIgnoreGravity(int viewId) {
244 mIgnoreGravity = viewId;
245 }
246
247 /**
Philip Milne1018fb42012-03-13 12:00:04 -0700248 * Describes how the child views are positioned.
249 *
250 * @return the gravity.
251 *
252 * @see #setGravity(int)
253 * @see android.view.Gravity
254 *
255 * @attr ref android.R.styleable#RelativeLayout_gravity
256 */
257 public int getGravity() {
258 return mGravity;
259 }
260
261 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 * Describes how the child views are positioned. Defaults to
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700263 * <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 *
Adam Powell1fec24e2011-09-15 14:19:30 -0700265 * <p>Note that since RelativeLayout considers the positioning of each child
266 * relative to one another to be significant, setting gravity will affect
267 * the positioning of all children as a single unit within the parent.
268 * This happens after children have been relatively positioned.</p>
269 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 * @param gravity See {@link android.view.Gravity}
271 *
272 * @see #setHorizontalGravity(int)
273 * @see #setVerticalGravity(int)
274 *
275 * @attr ref android.R.styleable#RelativeLayout_gravity
276 */
277 @android.view.RemotableViewMethod
278 public void setGravity(int gravity) {
279 if (mGravity != gravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700280 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700281 gravity |= Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 }
283
284 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
285 gravity |= Gravity.TOP;
286 }
287
288 mGravity = gravity;
289 requestLayout();
290 }
291 }
292
293 @android.view.RemotableViewMethod
294 public void setHorizontalGravity(int horizontalGravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700295 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
296 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
297 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 requestLayout();
299 }
300 }
301
302 @android.view.RemotableViewMethod
303 public void setVerticalGravity(int verticalGravity) {
304 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
305 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
306 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
307 requestLayout();
308 }
309 }
310
311 @Override
312 public int getBaseline() {
313 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
314 }
315
316 @Override
Romain Guy725015a2009-06-23 14:27:34 -0700317 public void requestLayout() {
318 super.requestLayout();
319 mDirtyHierarchy = true;
320 }
321
322 private void sortChildren() {
323 int count = getChildCount();
324 if (mSortedVerticalChildren.length != count) mSortedVerticalChildren = new View[count];
325 if (mSortedHorizontalChildren.length != count) mSortedHorizontalChildren = new View[count];
326
327 final DependencyGraph graph = mGraph;
328 graph.clear();
329
330 for (int i = 0; i < count; i++) {
331 final View child = getChildAt(i);
332 graph.add(child);
333 }
334
335 if (DEBUG_GRAPH) {
336 d(LOG_TAG, "=== Sorted vertical children");
Romain Guybc5d8762012-01-06 16:40:49 -0800337 graph.log(getResources(), RULES_VERTICAL);
Romain Guy725015a2009-06-23 14:27:34 -0700338 d(LOG_TAG, "=== Sorted horizontal children");
Romain Guybc5d8762012-01-06 16:40:49 -0800339 graph.log(getResources(), RULES_HORIZONTAL);
Romain Guy725015a2009-06-23 14:27:34 -0700340 }
341
Romain Guybc5d8762012-01-06 16:40:49 -0800342 graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
343 graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
Romain Guy9fffa1e2009-06-24 12:42:43 -0700344
345 if (DEBUG_GRAPH) {
346 d(LOG_TAG, "=== Ordered list of vertical children");
347 for (View view : mSortedVerticalChildren) {
348 DependencyGraph.printViewId(getResources(), view);
349 }
350 d(LOG_TAG, "=== Ordered list of horizontal children");
351 for (View view : mSortedHorizontalChildren) {
352 DependencyGraph.printViewId(getResources(), view);
353 }
354 }
Romain Guy725015a2009-06-23 14:27:34 -0700355 }
356
Romain Guy42460ac2010-01-11 16:46:33 -0800357 // TODO: we need to find another way to implement RelativeLayout
358 // This implementation cannot handle every case
Romain Guy725015a2009-06-23 14:27:34 -0700359 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Romain Guy725015a2009-06-23 14:27:34 -0700361 if (mDirtyHierarchy) {
362 mDirtyHierarchy = false;
363 sortChildren();
364 }
365
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 int myWidth = -1;
367 int myHeight = -1;
368
369 int width = 0;
370 int height = 0;
371
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800372 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
373 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
374 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
375 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376
377 // Record our dimensions if they are known;
378 if (widthMode != MeasureSpec.UNSPECIFIED) {
379 myWidth = widthSize;
380 }
381
382 if (heightMode != MeasureSpec.UNSPECIFIED) {
383 myHeight = heightSize;
384 }
385
386 if (widthMode == MeasureSpec.EXACTLY) {
387 width = myWidth;
388 }
389
390 if (heightMode == MeasureSpec.EXACTLY) {
391 height = myHeight;
392 }
393
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 mHasBaselineAlignedChild = false;
395
396 View ignore = null;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700397 int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700398 final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
400 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
401
402 int left = Integer.MAX_VALUE;
403 int top = Integer.MAX_VALUE;
404 int right = Integer.MIN_VALUE;
405 int bottom = Integer.MIN_VALUE;
406
Romain Guyf7dabb02009-06-25 14:47:14 -0700407 boolean offsetHorizontalAxis = false;
408 boolean offsetVerticalAxis = false;
409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
411 ignore = findViewById(mIgnoreGravity);
412 }
413
Romain Guyf7dabb02009-06-25 14:47:14 -0700414 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
415 final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
416
Romain Guye24ef602009-06-25 13:01:55 -0700417 View[] views = mSortedHorizontalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700418 int count = views.length;
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800419
420 // We need to know our size for doing the correct computation of positioning in RTL mode
421 if (isLayoutRtl() && (myWidth == -1 || isWrapContentWidth)) {
Fabrice Di Meglio4e46d0f2012-12-04 18:04:53 -0800422 int w = getPaddingStart() + getPaddingEnd();
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800423 final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
424 for (int i = 0; i < count; i++) {
425 View child = views[i];
426 if (child.getVisibility() != GONE) {
427 LayoutParams params = (LayoutParams) child.getLayoutParams();
428 // Would be similar to a call to measureChildHorizontal(child, params, -1, myHeight)
429 // but we cannot change for now the behavior of measureChildHorizontal() for
430 // taking care or a "-1" for "mywidth" so use here our own version of that code.
431 int childHeightMeasureSpec;
432 if (params.width == LayoutParams.MATCH_PARENT) {
433 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
434 } else {
435 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
436 }
437 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
438
Fabrice Di Meglio4e46d0f2012-12-04 18:04:53 -0800439 w += child.getMeasuredWidth();
440 w += params.leftMargin + params.rightMargin;
441 }
442 }
443 if (myWidth == -1) {
444 // Easy case: "myWidth" was undefined before so use the width we have just computed
445 myWidth = w;
446 } else {
447 // "myWidth" was defined before, so take the min of it and the computed width if it
448 // is a non null one
449 if (w > 0) {
450 myWidth = Math.min(myWidth, w);
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800451 }
452 }
453 }
454
Romain Guy725015a2009-06-23 14:27:34 -0700455 for (int i = 0; i < count; i++) {
456 View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 if (child.getVisibility() != GONE) {
458 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guy95607032009-06-24 14:37:03 -0700459
460 applyHorizontalSizeRules(params, myWidth);
Romain Guyf782e602009-06-25 15:26:49 -0700461 measureChildHorizontal(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700462 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
463 offsetHorizontalAxis = true;
464 }
Romain Guy725015a2009-06-23 14:27:34 -0700465 }
466 }
467
Romain Guye24ef602009-06-25 13:01:55 -0700468 views = mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700469 count = views.length;
Romain Guyf7dabb02009-06-25 14:47:14 -0700470
Romain Guy725015a2009-06-23 14:27:34 -0700471 for (int i = 0; i < count; i++) {
472 View child = views[i];
473 if (child.getVisibility() != GONE) {
474 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guy95607032009-06-24 14:37:03 -0700475
476 applyVerticalSizeRules(params, myHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 measureChild(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700478 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
479 offsetVerticalAxis = true;
480 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481
Romain Guyf7dabb02009-06-25 14:47:14 -0700482 if (isWrapContentWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 width = Math.max(width, params.mRight);
484 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700485
486 if (isWrapContentHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 height = Math.max(height, params.mBottom);
488 }
489
490 if (child != ignore || verticalGravity) {
491 left = Math.min(left, params.mLeft - params.leftMargin);
492 top = Math.min(top, params.mTop - params.topMargin);
493 }
494
495 if (child != ignore || horizontalGravity) {
496 right = Math.max(right, params.mRight + params.rightMargin);
497 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
498 }
499 }
500 }
501
502 if (mHasBaselineAlignedChild) {
Romain Guy725015a2009-06-23 14:27:34 -0700503 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 View child = getChildAt(i);
505 if (child.getVisibility() != GONE) {
506 LayoutParams params = (LayoutParams) child.getLayoutParams();
507 alignBaseline(child, params);
508
509 if (child != ignore || verticalGravity) {
Romain Guy725015a2009-06-23 14:27:34 -0700510 left = Math.min(left, params.mLeft - params.leftMargin);
511 top = Math.min(top, params.mTop - params.topMargin);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 }
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
Fabrice Di Megliobb4b6012012-10-26 16:27:55 -0700522 final int layoutDirection = getLayoutDirection();
523
Romain Guyf7dabb02009-06-25 14:47:14 -0700524 if (isWrapContentWidth) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700525 // Width already has left padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 // the right of each child view
527 width += mPaddingRight;
528
529 if (mLayoutParams.width >= 0) {
530 width = Math.max(width, mLayoutParams.width);
531 }
532
533 width = Math.max(width, getSuggestedMinimumWidth());
534 width = resolveSize(width, widthMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700535
536 if (offsetHorizontalAxis) {
Romain Guyd10a5762009-07-28 11:18:14 -0700537 for (int i = 0; i < count; i++) {
Romain Guyf7dabb02009-06-25 14:47:14 -0700538 View child = getChildAt(i);
539 if (child.getVisibility() != GONE) {
540 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700541 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700542 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
543 centerHorizontal(child, params, width);
Romain Guy42460ac2010-01-11 16:46:33 -0800544 } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
545 final int childWidth = child.getMeasuredWidth();
546 params.mLeft = width - mPaddingRight - childWidth;
547 params.mRight = params.mLeft + childWidth;
Romain Guyf7dabb02009-06-25 14:47:14 -0700548 }
549 }
550 }
551 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700553
554 if (isWrapContentHeight) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700555 // Height already has top padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 // the bottom of each child view
557 height += mPaddingBottom;
558
559 if (mLayoutParams.height >= 0) {
560 height = Math.max(height, mLayoutParams.height);
561 }
562
563 height = Math.max(height, getSuggestedMinimumHeight());
564 height = resolveSize(height, heightMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700565
566 if (offsetVerticalAxis) {
567 for (int i = 0; i < count; i++) {
568 View child = getChildAt(i);
569 if (child.getVisibility() != GONE) {
570 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700571 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700572 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
573 centerVertical(child, params, height);
Romain Guy42460ac2010-01-11 16:46:33 -0800574 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
575 final int childHeight = child.getMeasuredHeight();
576 params.mTop = height - mPaddingBottom - childHeight;
577 params.mBottom = params.mTop + childHeight;
Romain Guyf7dabb02009-06-25 14:47:14 -0700578 }
579 }
580 }
581 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 }
583
584 if (horizontalGravity || verticalGravity) {
585 final Rect selfBounds = mSelfBounds;
586 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
587 height - mPaddingBottom);
588
589 final Rect contentBounds = mContentBounds;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700590 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
Fabrice Di Meglioc0053222011-06-13 12:16:51 -0700591 layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592
593 final int horizontalOffset = contentBounds.left - left;
594 final int verticalOffset = contentBounds.top - top;
595 if (horizontalOffset != 0 || verticalOffset != 0) {
Romain Guy725015a2009-06-23 14:27:34 -0700596 for (int i = 0; i < count; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 View child = getChildAt(i);
598 if (child.getVisibility() != GONE && child != ignore) {
599 LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guyd10a5762009-07-28 11:18:14 -0700600 if (horizontalGravity) {
601 params.mLeft += horizontalOffset;
602 params.mRight += horizontalOffset;
603 }
604 if (verticalGravity) {
605 params.mTop += verticalOffset;
606 params.mBottom += verticalOffset;
607 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 }
609 }
610 }
611 }
612
613 setMeasuredDimension(width, height);
614 }
615
616 private void alignBaseline(View child, LayoutParams params) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700617 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700618 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE);
620
621 if (anchorBaseline != -1) {
622 LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE);
623 if (anchorParams != null) {
624 int offset = anchorParams.mTop + anchorBaseline;
625 int baseline = child.getBaseline();
626 if (baseline != -1) {
627 offset -= baseline;
628 }
629 int height = params.mBottom - params.mTop;
630 params.mTop = offset;
631 params.mBottom = params.mTop + height;
632 }
633 }
634
635 if (mBaselineView == null) {
636 mBaselineView = child;
637 } else {
638 LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams();
639 if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) {
640 mBaselineView = child;
641 }
642 }
643 }
644
645 /**
646 * Measure a child. The child should have left, top, right and bottom information
647 * stored in its LayoutParams. If any of these values is -1 it means that the view
648 * can extend up to the corresponding edge.
649 *
650 * @param child Child to measure
651 * @param params LayoutParams associated with child
652 * @param myWidth Width of the the RelativeLayout
653 * @param myHeight Height of the RelativeLayout
654 */
Romain Guy725015a2009-06-23 14:27:34 -0700655 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
657 params.mRight, params.width,
658 params.leftMargin, params.rightMargin,
659 mPaddingLeft, mPaddingRight,
660 myWidth);
661 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
662 params.mBottom, params.height,
663 params.topMargin, params.bottomMargin,
664 mPaddingTop, mPaddingBottom,
665 myHeight);
666 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
667 }
668
Romain Guyf782e602009-06-25 15:26:49 -0700669 private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
Romain Guy95607032009-06-24 14:37:03 -0700670 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
671 params.mRight, params.width,
672 params.leftMargin, params.rightMargin,
673 mPaddingLeft, mPaddingRight,
674 myWidth);
Romain Guyf782e602009-06-25 15:26:49 -0700675 int childHeightMeasureSpec;
Adam Powell946d05b2012-10-01 15:06:20 -0700676 if (params.width == LayoutParams.MATCH_PARENT) {
Romain Guybaac4632009-06-29 14:28:29 -0700677 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
Romain Guyf782e602009-06-25 15:26:49 -0700678 } else {
Romain Guybaac4632009-06-29 14:28:29 -0700679 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
Romain Guyf782e602009-06-25 15:26:49 -0700680 }
Romain Guy725015a2009-06-23 14:27:34 -0700681 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
682 }
683
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 /**
685 * Get a measure spec that accounts for all of the constraints on this view.
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700686 * This includes size constraints imposed by the RelativeLayout as well as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 * the View's desired dimension.
688 *
689 * @param childStart The left or top field of the child's layout params
690 * @param childEnd The right or bottom field of the child's layout params
691 * @param childSize The child's desired size (the width or height field of
692 * the child's layout params)
693 * @param startMargin The left or top margin
694 * @param endMargin The right or bottom margin
695 * @param startPadding mPaddingLeft or mPaddingTop
696 * @param endPadding mPaddingRight or mPaddingBottom
697 * @param mySize The width or height of this view (the RelativeLayout)
698 * @return MeasureSpec for the child
699 */
700 private int getChildMeasureSpec(int childStart, int childEnd,
701 int childSize, int startMargin, int endMargin, int startPadding,
702 int endPadding, int mySize) {
703 int childSpecMode = 0;
704 int childSpecSize = 0;
705
706 // Figure out start and end bounds.
707 int tempStart = childStart;
708 int tempEnd = childEnd;
709
710 // If the view did not express a layout constraint for an edge, use
711 // view's margins and our padding
712 if (tempStart < 0) {
713 tempStart = startPadding + startMargin;
714 }
715 if (tempEnd < 0) {
716 tempEnd = mySize - endPadding - endMargin;
717 }
718
719 // Figure out maximum size available to this view
720 int maxAvailable = tempEnd - tempStart;
721
722 if (childStart >= 0 && childEnd >= 0) {
723 // Constraints fixed both edges, so child must be an exact size
724 childSpecMode = MeasureSpec.EXACTLY;
725 childSpecSize = maxAvailable;
726 } else {
727 if (childSize >= 0) {
728 // Child wanted an exact size. Give as much as possible
729 childSpecMode = MeasureSpec.EXACTLY;
730
731 if (maxAvailable >= 0) {
732 // We have a maxmum size in this dimension.
733 childSpecSize = Math.min(maxAvailable, childSize);
734 } else {
735 // We can grow in this dimension.
736 childSpecSize = childSize;
737 }
Romain Guy980a9382010-01-08 15:06:28 -0800738 } else if (childSize == LayoutParams.MATCH_PARENT) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700739 // Child wanted to be as big as possible. Give all available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 // space
741 childSpecMode = MeasureSpec.EXACTLY;
742 childSpecSize = maxAvailable;
743 } else if (childSize == LayoutParams.WRAP_CONTENT) {
744 // Child wants to wrap content. Use AT_MOST
745 // to communicate available space if we know
746 // our max size
747 if (maxAvailable >= 0) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700748 // We have a maximum size in this dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 childSpecMode = MeasureSpec.AT_MOST;
750 childSpecSize = maxAvailable;
751 } else {
752 // We can grow in this dimension. Child can be as big as it
753 // wants
754 childSpecMode = MeasureSpec.UNSPECIFIED;
755 childSpecSize = 0;
756 }
757 }
758 }
759
760 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
761 }
762
Romain Guyf7dabb02009-06-25 14:47:14 -0700763 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
764 boolean wrapContent) {
765
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700766 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700767 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768
769 if (params.mLeft < 0 && params.mRight >= 0) {
770 // Right is fixed, but left varies
771 params.mLeft = params.mRight - child.getMeasuredWidth();
772 } else if (params.mLeft >= 0 && params.mRight < 0) {
773 // Left is fixed, but right varies
774 params.mRight = params.mLeft + child.getMeasuredWidth();
775 } else if (params.mLeft < 0 && params.mRight < 0) {
776 // Both left and right vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700777 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
778 if (!wrapContent) {
779 centerHorizontal(child, params, myWidth);
780 } else {
781 params.mLeft = mPaddingLeft + params.leftMargin;
782 params.mRight = params.mLeft + child.getMeasuredWidth();
783 }
784 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 } else {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700786 // This is the default case. For RTL we start from the right and for LTR we start
787 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
788 if (isLayoutRtl()) {
789 params.mRight = myWidth - mPaddingRight- params.rightMargin;
790 params.mLeft = params.mRight - child.getMeasuredWidth();
791 } else {
792 params.mLeft = mPaddingLeft + params.leftMargin;
793 params.mRight = params.mLeft + child.getMeasuredWidth();
794 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 }
796 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700797 return rules[ALIGN_PARENT_END] != 0;
Romain Guy725015a2009-06-23 14:27:34 -0700798 }
799
Romain Guyf7dabb02009-06-25 14:47:14 -0700800 private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
801 boolean wrapContent) {
802
Romain Guy725015a2009-06-23 14:27:34 -0700803 int[] rules = params.getRules();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804
805 if (params.mTop < 0 && params.mBottom >= 0) {
806 // Bottom is fixed, but top varies
807 params.mTop = params.mBottom - child.getMeasuredHeight();
808 } else if (params.mTop >= 0 && params.mBottom < 0) {
809 // Top is fixed, but bottom varies
810 params.mBottom = params.mTop + child.getMeasuredHeight();
811 } else if (params.mTop < 0 && params.mBottom < 0) {
812 // Both top and bottom vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700813 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
814 if (!wrapContent) {
815 centerVertical(child, params, myHeight);
816 } else {
817 params.mTop = mPaddingTop + params.topMargin;
818 params.mBottom = params.mTop + child.getMeasuredHeight();
819 }
820 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 } else {
822 params.mTop = mPaddingTop + params.topMargin;
823 params.mBottom = params.mTop + child.getMeasuredHeight();
824 }
825 }
Romain Guy42460ac2010-01-11 16:46:33 -0800826 return rules[ALIGN_PARENT_BOTTOM] != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 }
828
Romain Guy725015a2009-06-23 14:27:34 -0700829 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700830 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700831 int[] rules = childParams.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 RelativeLayout.LayoutParams anchorParams;
833
834 // -1 indicated a "soft requirement" in that direction. For example:
835 // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right
836 // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left
837 // left=10, right=20 means the left and right ends are both fixed
838 childParams.mLeft = -1;
839 childParams.mRight = -1;
840
841 anchorParams = getRelatedViewParams(rules, LEFT_OF);
842 if (anchorParams != null) {
843 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
844 childParams.rightMargin);
845 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
846 if (myWidth >= 0) {
847 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
848 } else {
849 // FIXME uh oh...
850 }
851 }
852
853 anchorParams = getRelatedViewParams(rules, RIGHT_OF);
854 if (anchorParams != null) {
855 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
856 childParams.leftMargin);
857 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
858 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
859 }
860
861 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
862 if (anchorParams != null) {
863 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
864 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
865 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
866 }
867
868 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
869 if (anchorParams != null) {
870 childParams.mRight = anchorParams.mRight - childParams.rightMargin;
871 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
872 if (myWidth >= 0) {
873 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
874 } else {
875 // FIXME uh oh...
876 }
877 }
878
879 if (0 != rules[ALIGN_PARENT_LEFT]) {
880 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
881 }
882
883 if (0 != rules[ALIGN_PARENT_RIGHT]) {
884 if (myWidth >= 0) {
885 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
886 } else {
887 // FIXME uh oh...
888 }
889 }
Romain Guy725015a2009-06-23 14:27:34 -0700890 }
891
892 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) {
893 int[] rules = childParams.getRules();
894 RelativeLayout.LayoutParams anchorParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895
896 childParams.mTop = -1;
897 childParams.mBottom = -1;
898
899 anchorParams = getRelatedViewParams(rules, ABOVE);
900 if (anchorParams != null) {
901 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
902 childParams.bottomMargin);
903 } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
904 if (myHeight >= 0) {
905 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
906 } else {
907 // FIXME uh oh...
908 }
909 }
910
911 anchorParams = getRelatedViewParams(rules, BELOW);
912 if (anchorParams != null) {
913 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
914 childParams.topMargin);
915 } else if (childParams.alignWithParent && rules[BELOW] != 0) {
916 childParams.mTop = mPaddingTop + childParams.topMargin;
917 }
918
919 anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
920 if (anchorParams != null) {
921 childParams.mTop = anchorParams.mTop + childParams.topMargin;
922 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
923 childParams.mTop = mPaddingTop + childParams.topMargin;
924 }
925
926 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
927 if (anchorParams != null) {
928 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
929 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
930 if (myHeight >= 0) {
931 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
932 } else {
933 // FIXME uh oh...
934 }
935 }
936
937 if (0 != rules[ALIGN_PARENT_TOP]) {
938 childParams.mTop = mPaddingTop + childParams.topMargin;
939 }
940
941 if (0 != rules[ALIGN_PARENT_BOTTOM]) {
942 if (myHeight >= 0) {
943 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
944 } else {
945 // FIXME uh oh...
946 }
947 }
948
949 if (rules[ALIGN_BASELINE] != 0) {
950 mHasBaselineAlignedChild = true;
951 }
952 }
953
954 private View getRelatedView(int[] rules, int relation) {
955 int id = rules[relation];
956 if (id != 0) {
Romain Guy1ab621e2009-06-25 13:31:57 -0700957 DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
Romain Guya0fd1d72009-06-24 14:25:43 -0700958 if (node == null) return null;
959 View v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960
961 // Find the first non-GONE view up the chain
962 while (v.getVisibility() == View.GONE) {
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800963 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
Romain Guy1ab621e2009-06-25 13:31:57 -0700964 node = mGraph.mKeyNodes.get((rules[relation]));
Romain Guya0fd1d72009-06-24 14:25:43 -0700965 if (node == null) return null;
966 v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 }
968
969 return v;
970 }
971
972 return null;
973 }
974
975 private LayoutParams getRelatedViewParams(int[] rules, int relation) {
976 View v = getRelatedView(rules, relation);
977 if (v != null) {
978 ViewGroup.LayoutParams params = v.getLayoutParams();
979 if (params instanceof LayoutParams) {
980 return (LayoutParams) v.getLayoutParams();
981 }
982 }
983 return null;
984 }
985
986 private int getRelatedViewBaseline(int[] rules, int relation) {
987 View v = getRelatedView(rules, relation);
988 if (v != null) {
989 return v.getBaseline();
990 }
991 return -1;
992 }
993
994 private void centerHorizontal(View child, LayoutParams params, int myWidth) {
995 int childWidth = child.getMeasuredWidth();
996 int left = (myWidth - childWidth) / 2;
997
998 params.mLeft = left;
999 params.mRight = left + childWidth;
1000 }
1001
1002 private void centerVertical(View child, LayoutParams params, int myHeight) {
1003 int childHeight = child.getMeasuredHeight();
1004 int top = (myHeight - childHeight) / 2;
1005
1006 params.mTop = top;
1007 params.mBottom = top + childHeight;
1008 }
1009
1010 @Override
1011 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1012 // The layout has actually already been performed and the positions
1013 // cached. Apply the cached values to the children.
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001014 final int count = getChildCount();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015
1016 for (int i = 0; i < count; i++) {
1017 View child = getChildAt(i);
1018 if (child.getVisibility() != GONE) {
1019 RelativeLayout.LayoutParams st =
1020 (RelativeLayout.LayoutParams) child.getLayoutParams();
1021 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 }
1023 }
1024 }
1025
1026 @Override
1027 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1028 return new RelativeLayout.LayoutParams(getContext(), attrs);
1029 }
1030
1031 /**
1032 * Returns a set of layout parameters with a width of
1033 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1034 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1035 */
1036 @Override
1037 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1038 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1039 }
1040
1041 // Override to allow type-checking of LayoutParams.
1042 @Override
1043 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1044 return p instanceof RelativeLayout.LayoutParams;
1045 }
1046
1047 @Override
1048 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1049 return new LayoutParams(p);
1050 }
1051
svetoslavganov75986cf2009-05-14 22:28:01 -07001052 @Override
1053 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1054 if (mTopToBottomLeftToRightSet == null) {
1055 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1056 }
1057
1058 // sort children top-to-bottom and left-to-right
1059 for (int i = 0, count = getChildCount(); i < count; i++) {
1060 mTopToBottomLeftToRightSet.add(getChildAt(i));
1061 }
1062
1063 for (View view : mTopToBottomLeftToRightSet) {
Svetoslav Ganov0b0a41d2011-09-07 18:06:03 -07001064 if (view.getVisibility() == View.VISIBLE
1065 && view.dispatchPopulateAccessibilityEvent(event)) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001066 mTopToBottomLeftToRightSet.clear();
1067 return true;
1068 }
1069 }
1070
1071 mTopToBottomLeftToRightSet.clear();
1072 return false;
1073 }
1074
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001075 @Override
1076 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1077 super.onInitializeAccessibilityEvent(event);
1078 event.setClassName(RelativeLayout.class.getName());
1079 }
1080
1081 @Override
1082 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1083 super.onInitializeAccessibilityNodeInfo(info);
1084 info.setClassName(RelativeLayout.class.getName());
1085 }
1086
svetoslavganov75986cf2009-05-14 22:28:01 -07001087 /**
1088 * Compares two views in left-to-right and top-to-bottom fashion.
1089 */
1090 private class TopToBottomLeftToRightComparator implements Comparator<View> {
1091 public int compare(View first, View second) {
1092 // top - bottom
1093 int topDifference = first.getTop() - second.getTop();
1094 if (topDifference != 0) {
1095 return topDifference;
1096 }
1097 // left - right
1098 int leftDifference = first.getLeft() - second.getLeft();
1099 if (leftDifference != 0) {
1100 return leftDifference;
1101 }
1102 // break tie by height
1103 int heightDiference = first.getHeight() - second.getHeight();
1104 if (heightDiference != 0) {
1105 return heightDiference;
1106 }
1107 // break tie by width
1108 int widthDiference = first.getWidth() - second.getWidth();
1109 if (widthDiference != 0) {
1110 return widthDiference;
1111 }
1112 return 0;
1113 }
1114 }
1115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 /**
1117 * Per-child layout information associated with RelativeLayout.
1118 *
1119 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1120 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1121 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1122 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1123 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1124 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1125 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1126 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1127 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1128 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1129 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1130 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1131 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1132 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1133 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1134 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1135 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001136 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1137 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1138 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1139 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1140 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1141 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 */
1143 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001144 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001145 @ViewDebug.IntToString(from = ABOVE, to = "above"),
1146 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
1147 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
1148 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"),
1149 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1150 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"),
1151 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"),
1152 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"),
1153 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"),
1154 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"),
1155 @ViewDebug.IntToString(from = BELOW, to = "below"),
1156 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"),
1157 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"),
1158 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"),
1159 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"),
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001160 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"),
1161 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"),
1162 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"),
1163 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"),
1164 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"),
1165 @ViewDebug.IntToString(from = START_OF, to = "startOf"),
1166 @ViewDebug.IntToString(from = END_OF, to = "endOf")
The Android Open Source Project10592532009-03-18 17:39:46 -07001167 }, mapping = {
1168 @ViewDebug.IntToString(from = TRUE, to = "true"),
Romain Guya1f3e4a2009-06-04 15:10:46 -07001169 @ViewDebug.IntToString(from = 0, to = "false/NO_ID")
The Android Open Source Project10592532009-03-18 17:39:46 -07001170 })
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 private int[] mRules = new int[VERB_COUNT];
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001173 private int[] mInitialRules = new int[VERB_COUNT];
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001174
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 private int mLeft, mTop, mRight, mBottom;
1176
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001177 private int mStart = DEFAULT_RELATIVE;
1178 private int mEnd = DEFAULT_RELATIVE;
1179
1180 private boolean mRulesChanged = false;
1181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 /**
1183 * When true, uses the parent as the anchor if the anchor doesn't exist or if
1184 * the anchor's visibility is GONE.
1185 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001186 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 public boolean alignWithParent;
1188
1189 public LayoutParams(Context c, AttributeSet attrs) {
1190 super(c, attrs);
1191
1192 TypedArray a = c.obtainStyledAttributes(attrs,
1193 com.android.internal.R.styleable.RelativeLayout_Layout);
1194
1195 final int[] rules = mRules;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001196 final int[] initialRules = mInitialRules;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197
1198 final int N = a.getIndexCount();
1199 for (int i = 0; i < N; i++) {
1200 int attr = a.getIndex(i);
1201 switch (attr) {
1202 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1203 alignWithParent = a.getBoolean(attr, false);
1204 break;
1205 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1206 rules[LEFT_OF] = a.getResourceId(attr, 0);
1207 break;
1208 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1209 rules[RIGHT_OF] = a.getResourceId(attr, 0);
1210 break;
1211 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1212 rules[ABOVE] = a.getResourceId(attr, 0);
1213 break;
1214 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1215 rules[BELOW] = a.getResourceId(attr, 0);
1216 break;
1217 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1218 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1219 break;
1220 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1221 rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1222 break;
1223 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1224 rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1225 break;
1226 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1227 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1228 break;
1229 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1230 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1231 break;
1232 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1233 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1234 break;
1235 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1236 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1237 break;
1238 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1239 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1240 break;
1241 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1242 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1243 break;
1244 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1245 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1246 break;
1247 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1248 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1249 break;
1250 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1251 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1252 break;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001253 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1254 rules[START_OF] = a.getResourceId(attr, 0);
1255 break;
1256 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1257 rules[END_OF] = a.getResourceId(attr, 0);
1258 break;
1259 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1260 rules[ALIGN_START] = a.getResourceId(attr, 0);
1261 break;
1262 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1263 rules[ALIGN_END] = a.getResourceId(attr, 0);
1264 break;
1265 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1266 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1267 break;
1268 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1269 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1270 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 }
1272 }
1273
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001274 for (int n = LEFT_OF; n < VERB_COUNT; n++) {
1275 initialRules[n] = rules[n];
1276 }
1277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278 a.recycle();
1279 }
1280
1281 public LayoutParams(int w, int h) {
1282 super(w, h);
1283 }
1284
1285 /**
1286 * {@inheritDoc}
1287 */
1288 public LayoutParams(ViewGroup.LayoutParams source) {
1289 super(source);
1290 }
1291
1292 /**
1293 * {@inheritDoc}
1294 */
1295 public LayoutParams(ViewGroup.MarginLayoutParams source) {
1296 super(source);
1297 }
1298
1299 @Override
1300 public String debug(String output) {
1301 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1302 ", height=" + sizeToString(height) + " }";
1303 }
1304
1305 /**
1306 * Adds a layout rule to be interpreted by the RelativeLayout. This
1307 * method should only be used for constraints that don't refer to another sibling
1308 * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE}
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001309 * 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 -08001310 * {@link #addRule(int, int)} instead.
1311 *
1312 * @param verb One of the verbs defined by
1313 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1314 * ALIGN_WITH_PARENT_LEFT.
1315 * @see #addRule(int, int)
1316 */
1317 public void addRule(int verb) {
1318 mRules[verb] = TRUE;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001319 mInitialRules[verb] = TRUE;
1320 mRulesChanged = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 }
1322
1323 /**
1324 * Adds a layout rule to be interpreted by the RelativeLayout. Use this for
1325 * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
1326 * value (VISIBLE).
1327 *
1328 * @param verb One of the verbs defined by
1329 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1330 * ALIGN_WITH_PARENT_LEFT.
1331 * @param anchor The id of another view to use as an anchor,
1332 * or a boolean value(represented as {@link RelativeLayout#TRUE})
1333 * for true or 0 for false). For verbs that don't refer to another sibling
1334 * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
1335 * @see #addRule(int)
1336 */
1337 public void addRule(int verb, int anchor) {
1338 mRules[verb] = anchor;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001339 mInitialRules[verb] = anchor;
1340 mRulesChanged = true;
1341 }
1342
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001343 /**
1344 * Removes a layout rule to be interpreted by the RelativeLayout.
1345 *
1346 * @param verb One of the verbs defined by
1347 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1348 * ALIGN_WITH_PARENT_LEFT.
1349 * @see #addRule(int)
1350 * @see #addRule(int, int)
1351 */
1352 public void removeRule(int verb) {
1353 mRules[verb] = 0;
1354 mInitialRules[verb] = 0;
1355 mRulesChanged = true;
1356 }
1357
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001358 private boolean hasRelativeRules() {
1359 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1360 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1361 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1362 }
1363
1364 private void resolveRules(int layoutDirection) {
1365 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
1366 // Reset to initial state
1367 for (int n = LEFT_OF; n < VERB_COUNT; n++) {
1368 mRules[n] = mInitialRules[n];
1369 }
1370 // Apply rules depending on direction
1371 if (mRules[ALIGN_START] != 0) {
1372 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1373 }
1374 if (mRules[ALIGN_END] != 0) {
1375 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1376 }
1377 if (mRules[START_OF] != 0) {
1378 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1379 }
1380 if (mRules[END_OF] != 0) {
1381 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1382 }
1383 if (mRules[ALIGN_PARENT_START] != 0) {
1384 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1385 }
1386 if (mRules[ALIGN_PARENT_END] != 0) {
1387 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1388 }
1389 mRulesChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 }
1391
1392 /**
1393 * Retrieves a complete list of all supported rules, where the index is the rule
1394 * verb, and the element value is the value specified, or "false" if it was never
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001395 * set. If there are relative rules defined (*_START / *_END), they will be resolved
1396 * depending on the layout direction.
1397 *
1398 * @param layoutDirection the direction of the layout.
1399 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
1400 * or {@link View#LAYOUT_DIRECTION_RTL}
1401 * @return the supported rules
1402 * @see #addRule(int, int)
1403 *
1404 * @hide
1405 */
1406 public int[] getRules(int layoutDirection) {
1407 if (hasRelativeRules() &&
1408 (mRulesChanged || layoutDirection != getLayoutDirection())) {
1409 resolveRules(layoutDirection);
1410 if (layoutDirection != getLayoutDirection()) {
1411 setLayoutDirection(layoutDirection);
1412 }
1413 }
1414 return mRules;
1415 }
1416
1417 /**
1418 * Retrieves a complete list of all supported rules, where the index is the rule
1419 * verb, and the element value is the value specified, or "false" if it was never
1420 * set. There will be no resolution of relative rules done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001421 *
1422 * @return the supported rules
1423 * @see #addRule(int, int)
1424 */
1425 public int[] getRules() {
1426 return mRules;
1427 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001428
1429 @Override
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001430 public void resolveLayoutDirection(int layoutDirection) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001431 final boolean isLayoutRtl = isLayoutRtl();
1432 if (isLayoutRtl) {
1433 if (mStart != DEFAULT_RELATIVE) mRight = mStart;
1434 if (mEnd != DEFAULT_RELATIVE) mLeft = mEnd;
1435 } else {
1436 if (mStart != DEFAULT_RELATIVE) mLeft = mStart;
1437 if (mEnd != DEFAULT_RELATIVE) mRight = mEnd;
1438 }
1439
1440 if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
1441 resolveRules(layoutDirection);
1442 }
1443 // This will set the layout direction
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001444 super.resolveLayoutDirection(layoutDirection);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001445 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 }
Romain Guy725015a2009-06-23 14:27:34 -07001447
1448 private static class DependencyGraph {
1449 /**
Romain Guy1ab621e2009-06-25 13:31:57 -07001450 * List of all views in the graph.
1451 */
1452 private ArrayList<Node> mNodes = new ArrayList<Node>();
1453
1454 /**
Romain Guy725015a2009-06-23 14:27:34 -07001455 * List of nodes in the graph. Each node is identified by its
1456 * view id (see View#getId()).
1457 */
Romain Guy1ab621e2009-06-25 13:31:57 -07001458 private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001459
1460 /**
1461 * Temporary data structure used to build the list of roots
1462 * for this graph.
1463 */
Romain Guybc5d8762012-01-06 16:40:49 -08001464 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001465
1466 /**
1467 * Clears the graph.
1468 */
1469 void clear() {
Romain Guy1ab621e2009-06-25 13:31:57 -07001470 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001471 final int count = nodes.size();
1472
1473 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001474 nodes.get(i).release();
Romain Guy725015a2009-06-23 14:27:34 -07001475 }
1476 nodes.clear();
1477
Romain Guy1ab621e2009-06-25 13:31:57 -07001478 mKeyNodes.clear();
Romain Guy725015a2009-06-23 14:27:34 -07001479 mRoots.clear();
1480 }
1481
1482 /**
1483 * Adds a view to the graph.
1484 *
1485 * @param view The view to be added as a node to the graph.
1486 */
1487 void add(View view) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001488 final int id = view.getId();
1489 final Node node = Node.acquire(view);
1490
1491 if (id != View.NO_ID) {
1492 mKeyNodes.put(id, node);
1493 }
1494
1495 mNodes.add(node);
Romain Guy725015a2009-06-23 14:27:34 -07001496 }
1497
1498 /**
1499 * Builds a sorted list of views. The sorting order depends on the dependencies
1500 * between the view. For instance, if view C needs view A to be processed first
1501 * and view A needs view B to be processed first, the dependency graph
1502 * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1503 *
1504 * @param sorted The sorted list of views. The length of this array must
1505 * be equal to getChildCount().
1506 * @param rules The list of rules to take into account.
1507 */
1508 void getSortedViews(View[] sorted, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001509 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001510 int index = 0;
1511
Romain Guybc5d8762012-01-06 16:40:49 -08001512 Node node;
1513 while ((node = roots.pollLast()) != null) {
Romain Guy725015a2009-06-23 14:27:34 -07001514 final View view = node.view;
1515 final int key = view.getId();
1516
1517 sorted[index++] = view;
1518
Romain Guybc5d8762012-01-06 16:40:49 -08001519 final HashMap<Node, DependencyGraph> dependents = node.dependents;
1520 for (Node dependent : dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001521 final SparseArray<Node> dependencies = dependent.dependencies;
1522
1523 dependencies.remove(key);
1524 if (dependencies.size() == 0) {
1525 roots.add(dependent);
1526 }
1527 }
1528 }
1529
1530 if (index < sorted.length) {
1531 throw new IllegalStateException("Circular dependencies cannot exist"
1532 + " in RelativeLayout");
1533 }
1534 }
1535
1536 /**
1537 * Finds the roots of the graph. A root is a node with no dependency and
1538 * with [0..n] dependents.
1539 *
1540 * @param rulesFilter The list of rules to consider when building the
1541 * dependencies
1542 *
1543 * @return A list of node, each being a root of the graph
1544 */
Romain Guybc5d8762012-01-06 16:40:49 -08001545 private ArrayDeque<Node> findRoots(int[] rulesFilter) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001546 final SparseArray<Node> keyNodes = mKeyNodes;
1547 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001548 final int count = nodes.size();
1549
1550 // Find roots can be invoked several times, so make sure to clear
1551 // all dependents and dependencies before running the algorithm
1552 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001553 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001554 node.dependents.clear();
1555 node.dependencies.clear();
1556 }
1557
1558 // Builds up the dependents and dependencies for each node of the graph
1559 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001560 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001561
1562 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1563 final int[] rules = layoutParams.mRules;
1564 final int rulesCount = rulesFilter.length;
1565
1566 // Look only the the rules passed in parameter, this way we build only the
1567 // dependencies for a specific set of rules
1568 for (int j = 0; j < rulesCount; j++) {
1569 final int rule = rules[rulesFilter[j]];
1570 if (rule > 0) {
1571 // The node this node depends on
Romain Guy1ab621e2009-06-25 13:31:57 -07001572 final Node dependency = keyNodes.get(rule);
Romain Guyda3003e2009-07-19 19:47:42 -07001573 // Skip unknowns and self dependencies
1574 if (dependency == null || dependency == node) {
Romain Guyb8f8de82009-06-25 12:03:56 -07001575 continue;
1576 }
Romain Guy725015a2009-06-23 14:27:34 -07001577 // Add the current node as a dependent
Romain Guybc5d8762012-01-06 16:40:49 -08001578 dependency.dependents.put(node, this);
Romain Guy725015a2009-06-23 14:27:34 -07001579 // Add a dependency to the current node
1580 node.dependencies.put(rule, dependency);
1581 }
1582 }
1583 }
1584
Romain Guybc5d8762012-01-06 16:40:49 -08001585 final ArrayDeque<Node> roots = mRoots;
Romain Guy725015a2009-06-23 14:27:34 -07001586 roots.clear();
1587
1588 // Finds all the roots in the graph: all nodes with no dependencies
1589 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001590 final Node node = nodes.get(i);
Romain Guybc5d8762012-01-06 16:40:49 -08001591 if (node.dependencies.size() == 0) roots.addLast(node);
Romain Guy725015a2009-06-23 14:27:34 -07001592 }
1593
1594 return roots;
1595 }
1596
1597 /**
1598 * Prints the dependency graph for the specified rules.
1599 *
1600 * @param resources The context's resources to print the ids.
1601 * @param rules The list of rules to take into account.
1602 */
1603 void log(Resources resources, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001604 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001605 for (Node node : roots) {
1606 printNode(resources, node);
1607 }
1608 }
1609
Romain Guy9fffa1e2009-06-24 12:42:43 -07001610 static void printViewId(Resources resources, View view) {
Romain Guy725015a2009-06-23 14:27:34 -07001611 if (view.getId() != View.NO_ID) {
1612 d(LOG_TAG, resources.getResourceEntryName(view.getId()));
1613 } else {
1614 d(LOG_TAG, "NO_ID");
1615 }
1616 }
1617
1618 private static void appendViewId(Resources resources, Node node, StringBuilder buffer) {
1619 if (node.view.getId() != View.NO_ID) {
1620 buffer.append(resources.getResourceEntryName(node.view.getId()));
1621 } else {
1622 buffer.append("NO_ID");
1623 }
1624 }
1625
1626 private static void printNode(Resources resources, Node node) {
1627 if (node.dependents.size() == 0) {
1628 printViewId(resources, node.view);
1629 } else {
Romain Guybc5d8762012-01-06 16:40:49 -08001630 for (Node dependent : node.dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001631 StringBuilder buffer = new StringBuilder();
1632 appendViewId(resources, node, buffer);
1633 printdependents(resources, dependent, buffer);
1634 }
1635 }
1636 }
1637
1638 private static void printdependents(Resources resources, Node node, StringBuilder buffer) {
1639 buffer.append(" -> ");
1640 appendViewId(resources, node, buffer);
1641
1642 if (node.dependents.size() == 0) {
1643 d(LOG_TAG, buffer.toString());
1644 } else {
Romain Guybc5d8762012-01-06 16:40:49 -08001645 for (Node dependent : node.dependents.keySet()) {
Romain Guy725015a2009-06-23 14:27:34 -07001646 StringBuilder subBuffer = new StringBuilder(buffer);
1647 printdependents(resources, dependent, subBuffer);
1648 }
1649 }
1650 }
1651
1652 /**
1653 * A node in the dependency graph. A node is a view, its list of dependencies
1654 * and its list of dependents.
1655 *
1656 * A node with no dependent is considered a root of the graph.
1657 */
1658 static class Node implements Poolable<Node> {
1659 /**
1660 * The view representing this node in the layout.
1661 */
1662 View view;
1663
1664 /**
1665 * The list of dependents for this node; a dependent is a node
1666 * that needs this node to be processed first.
1667 */
Romain Guybc5d8762012-01-06 16:40:49 -08001668 final HashMap<Node, DependencyGraph> dependents = new HashMap<Node, DependencyGraph>();
Romain Guy725015a2009-06-23 14:27:34 -07001669
1670 /**
1671 * The list of dependencies for this node.
1672 */
1673 final SparseArray<Node> dependencies = new SparseArray<Node>();
1674
1675 /*
1676 * START POOL IMPLEMENTATION
1677 */
Romain Guybaac4632009-06-29 14:28:29 -07001678 // The pool is static, so all nodes instances are shared across
1679 // activities, that's why we give it a rather high limit
1680 private static final int POOL_LIMIT = 100;
Romain Guy725015a2009-06-23 14:27:34 -07001681 private static final Pool<Node> sPool = Pools.synchronizedPool(
1682 Pools.finitePool(new PoolableManager<Node>() {
1683 public Node newInstance() {
1684 return new Node();
1685 }
1686
1687 public void onAcquired(Node element) {
1688 }
1689
1690 public void onReleased(Node element) {
1691 }
1692 }, POOL_LIMIT)
1693 );
1694
1695 private Node mNext;
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001696 private boolean mIsPooled;
Romain Guy725015a2009-06-23 14:27:34 -07001697
1698 public void setNextPoolable(Node element) {
1699 mNext = element;
1700 }
1701
1702 public Node getNextPoolable() {
1703 return mNext;
1704 }
1705
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001706 public boolean isPooled() {
1707 return mIsPooled;
1708 }
1709
1710 public void setPooled(boolean isPooled) {
1711 mIsPooled = isPooled;
1712 }
1713
Romain Guy725015a2009-06-23 14:27:34 -07001714 static Node acquire(View view) {
1715 final Node node = sPool.acquire();
1716 node.view = view;
1717
1718 return node;
1719 }
1720
1721 void release() {
1722 view = null;
1723 dependents.clear();
1724 dependencies.clear();
1725
1726 sPool.release(this);
1727 }
1728 /*
1729 * END POOL IMPLEMENTATION
1730 */
1731 }
1732 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733}