• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.widget;
18 
19 import android.util.ArrayMap;
20 import com.android.internal.R;
21 
22 import java.util.ArrayDeque;
23 import java.util.ArrayList;
24 import java.util.Comparator;
25 import java.util.SortedSet;
26 import java.util.TreeSet;
27 
28 import android.content.Context;
29 import android.content.res.TypedArray;
30 import android.graphics.Rect;
31 import android.os.Build;
32 import android.util.AttributeSet;
33 import android.util.Pools.SynchronizedPool;
34 import android.util.SparseArray;
35 import android.view.Gravity;
36 import android.view.View;
37 import android.view.ViewDebug;
38 import android.view.ViewGroup;
39 import android.view.accessibility.AccessibilityEvent;
40 import android.view.accessibility.AccessibilityNodeInfo;
41 import android.widget.RemoteViews.RemoteView;
42 
43 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
44 
45 /**
46  * A Layout where the positions of the children can be described in relation to each other or to the
47  * parent.
48  *
49  * <p>
50  * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
51  * position of its children. For example, you cannot have a RelativeLayout whose height is set to
52  * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
53  * {@link #ALIGN_PARENT_BOTTOM}.
54  * </p>
55  *
56  * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by
57  * a measurement bug that could cause child views to be measured with incorrect
58  * {@link android.view.View.MeasureSpec MeasureSpec} values. (See
59  * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec}
60  * for more details.) This was triggered when a RelativeLayout container was placed in
61  * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view
62  * not equipped to properly measure with the MeasureSpec mode
63  * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout,
64  * this would silently work anyway as RelativeLayout would pass a very large
65  * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p>
66  *
67  * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
68  * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
69  * version 18 or newer will receive the correct behavior</p>
70  *
71  * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
72  * Layout</a> guide.</p>
73  *
74  * <p>
75  * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
76  * layout attributes
77  * </p>
78  *
79  * @attr ref android.R.styleable#RelativeLayout_gravity
80  * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
81  */
82 @RemoteView
83 public class RelativeLayout extends ViewGroup {
84     public static final int TRUE = -1;
85 
86     /**
87      * Rule that aligns a child's right edge with another child's left edge.
88      */
89     public static final int LEFT_OF                  = 0;
90     /**
91      * Rule that aligns a child's left edge with another child's right edge.
92      */
93     public static final int RIGHT_OF                 = 1;
94     /**
95      * Rule that aligns a child's bottom edge with another child's top edge.
96      */
97     public static final int ABOVE                    = 2;
98     /**
99      * Rule that aligns a child's top edge with another child's bottom edge.
100      */
101     public static final int BELOW                    = 3;
102 
103     /**
104      * Rule that aligns a child's baseline with another child's baseline.
105      */
106     public static final int ALIGN_BASELINE           = 4;
107     /**
108      * Rule that aligns a child's left edge with another child's left edge.
109      */
110     public static final int ALIGN_LEFT               = 5;
111     /**
112      * Rule that aligns a child's top edge with another child's top edge.
113      */
114     public static final int ALIGN_TOP                = 6;
115     /**
116      * Rule that aligns a child's right edge with another child's right edge.
117      */
118     public static final int ALIGN_RIGHT              = 7;
119     /**
120      * Rule that aligns a child's bottom edge with another child's bottom edge.
121      */
122     public static final int ALIGN_BOTTOM             = 8;
123 
124     /**
125      * Rule that aligns the child's left edge with its RelativeLayout
126      * parent's left edge.
127      */
128     public static final int ALIGN_PARENT_LEFT        = 9;
129     /**
130      * Rule that aligns the child's top edge with its RelativeLayout
131      * parent's top edge.
132      */
133     public static final int ALIGN_PARENT_TOP         = 10;
134     /**
135      * Rule that aligns the child's right edge with its RelativeLayout
136      * parent's right edge.
137      */
138     public static final int ALIGN_PARENT_RIGHT       = 11;
139     /**
140      * Rule that aligns the child's bottom edge with its RelativeLayout
141      * parent's bottom edge.
142      */
143     public static final int ALIGN_PARENT_BOTTOM      = 12;
144 
145     /**
146      * Rule that centers the child with respect to the bounds of its
147      * RelativeLayout parent.
148      */
149     public static final int CENTER_IN_PARENT         = 13;
150     /**
151      * Rule that centers the child horizontally with respect to the
152      * bounds of its RelativeLayout parent.
153      */
154     public static final int CENTER_HORIZONTAL        = 14;
155     /**
156      * Rule that centers the child vertically with respect to the
157      * bounds of its RelativeLayout parent.
158      */
159     public static final int CENTER_VERTICAL          = 15;
160     /**
161      * Rule that aligns a child's end edge with another child's start edge.
162      */
163     public static final int START_OF                 = 16;
164     /**
165      * Rule that aligns a child's start edge with another child's end edge.
166      */
167     public static final int END_OF                   = 17;
168     /**
169      * Rule that aligns a child's start edge with another child's start edge.
170      */
171     public static final int ALIGN_START              = 18;
172     /**
173      * Rule that aligns a child's end edge with another child's end edge.
174      */
175     public static final int ALIGN_END                = 19;
176     /**
177      * Rule that aligns the child's start edge with its RelativeLayout
178      * parent's start edge.
179      */
180     public static final int ALIGN_PARENT_START       = 20;
181     /**
182      * Rule that aligns the child's end edge with its RelativeLayout
183      * parent's end edge.
184      */
185     public static final int ALIGN_PARENT_END         = 21;
186 
187     private static final int VERB_COUNT              = 22;
188 
189 
190     private static final int[] RULES_VERTICAL = {
191             ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
192     };
193 
194     private static final int[] RULES_HORIZONTAL = {
195             LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
196     };
197 
198     /**
199      * Used to indicate left/right/top/bottom should be inferred from constraints
200      */
201     private static final int VALUE_NOT_SET = Integer.MIN_VALUE;
202 
203     private View mBaselineView = null;
204     private boolean mHasBaselineAlignedChild;
205 
206     private int mGravity = Gravity.START | Gravity.TOP;
207     private final Rect mContentBounds = new Rect();
208     private final Rect mSelfBounds = new Rect();
209     private int mIgnoreGravity;
210 
211     private SortedSet<View> mTopToBottomLeftToRightSet = null;
212 
213     private boolean mDirtyHierarchy;
214     private View[] mSortedHorizontalChildren;
215     private View[] mSortedVerticalChildren;
216     private final DependencyGraph mGraph = new DependencyGraph();
217 
218     // Compatibility hack. Old versions of the platform had problems
219     // with MeasureSpec value overflow and RelativeLayout was one source of them.
220     // Some apps came to rely on them. :(
221     private boolean mAllowBrokenMeasureSpecs = false;
222     // Compatibility hack. Old versions of the platform would not take
223     // margins and padding into account when generating the height measure spec
224     // for children during the horizontal measure pass.
225     private boolean mMeasureVerticalWithPaddingMargin = false;
226 
227     // A default width used for RTL measure pass
228     /**
229      * Value reduced so as not to interfere with View's measurement spec. flags. See:
230      * {@link View#MEASURED_SIZE_MASK}.
231      * {@link View#MEASURED_STATE_TOO_SMALL}.
232      **/
233     private static final int DEFAULT_WIDTH = 0x00010000;
234 
RelativeLayout(Context context)235     public RelativeLayout(Context context) {
236         this(context, null);
237     }
238 
RelativeLayout(Context context, AttributeSet attrs)239     public RelativeLayout(Context context, AttributeSet attrs) {
240         this(context, attrs, 0);
241     }
242 
RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr)243     public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
244         this(context, attrs, defStyleAttr, 0);
245     }
246 
RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)247     public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
248         super(context, attrs, defStyleAttr, defStyleRes);
249         initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
250         queryCompatibilityModes(context);
251     }
252 
initFromAttributes( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)253     private void initFromAttributes(
254             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
255         final TypedArray a = context.obtainStyledAttributes(
256                 attrs, R.styleable.RelativeLayout, defStyleAttr, defStyleRes);
257         mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
258         mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
259         a.recycle();
260     }
261 
queryCompatibilityModes(Context context)262     private void queryCompatibilityModes(Context context) {
263         int version = context.getApplicationInfo().targetSdkVersion;
264         mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
265         mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
266     }
267 
268     @Override
shouldDelayChildPressedState()269     public boolean shouldDelayChildPressedState() {
270         return false;
271     }
272 
273     /**
274      * Defines which View is ignored when the gravity is applied. This setting has no
275      * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
276      *
277      * @param viewId The id of the View to be ignored by gravity, or 0 if no View
278      *        should be ignored.
279      *
280      * @see #setGravity(int)
281      *
282      * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
283      */
284     @android.view.RemotableViewMethod
setIgnoreGravity(int viewId)285     public void setIgnoreGravity(int viewId) {
286         mIgnoreGravity = viewId;
287     }
288 
289     /**
290      * Describes how the child views are positioned.
291      *
292      * @return the gravity.
293      *
294      * @see #setGravity(int)
295      * @see android.view.Gravity
296      *
297      * @attr ref android.R.styleable#RelativeLayout_gravity
298      */
getGravity()299     public int getGravity() {
300         return mGravity;
301     }
302 
303     /**
304      * Describes how the child views are positioned. Defaults to
305      * <code>Gravity.START | Gravity.TOP</code>.
306      *
307      * <p>Note that since RelativeLayout considers the positioning of each child
308      * relative to one another to be significant, setting gravity will affect
309      * the positioning of all children as a single unit within the parent.
310      * This happens after children have been relatively positioned.</p>
311      *
312      * @param gravity See {@link android.view.Gravity}
313      *
314      * @see #setHorizontalGravity(int)
315      * @see #setVerticalGravity(int)
316      *
317      * @attr ref android.R.styleable#RelativeLayout_gravity
318      */
319     @android.view.RemotableViewMethod
setGravity(int gravity)320     public void setGravity(int gravity) {
321         if (mGravity != gravity) {
322             if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
323                 gravity |= Gravity.START;
324             }
325 
326             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
327                 gravity |= Gravity.TOP;
328             }
329 
330             mGravity = gravity;
331             requestLayout();
332         }
333     }
334 
335     @android.view.RemotableViewMethod
setHorizontalGravity(int horizontalGravity)336     public void setHorizontalGravity(int horizontalGravity) {
337         final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
338         if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
339             mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
340             requestLayout();
341         }
342     }
343 
344     @android.view.RemotableViewMethod
setVerticalGravity(int verticalGravity)345     public void setVerticalGravity(int verticalGravity) {
346         final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
347         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
348             mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
349             requestLayout();
350         }
351     }
352 
353     @Override
getBaseline()354     public int getBaseline() {
355         return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
356     }
357 
358     @Override
requestLayout()359     public void requestLayout() {
360         super.requestLayout();
361         mDirtyHierarchy = true;
362     }
363 
sortChildren()364     private void sortChildren() {
365         final int count = getChildCount();
366         if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
367             mSortedVerticalChildren = new View[count];
368         }
369 
370         if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
371             mSortedHorizontalChildren = new View[count];
372         }
373 
374         final DependencyGraph graph = mGraph;
375         graph.clear();
376 
377         for (int i = 0; i < count; i++) {
378             graph.add(getChildAt(i));
379         }
380 
381         graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
382         graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
383     }
384 
385     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)386     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
387         if (mDirtyHierarchy) {
388             mDirtyHierarchy = false;
389             sortChildren();
390         }
391 
392         int myWidth = -1;
393         int myHeight = -1;
394 
395         int width = 0;
396         int height = 0;
397 
398         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
399         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
400         final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
401         final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
402 
403         // Record our dimensions if they are known;
404         if (widthMode != MeasureSpec.UNSPECIFIED) {
405             myWidth = widthSize;
406         }
407 
408         if (heightMode != MeasureSpec.UNSPECIFIED) {
409             myHeight = heightSize;
410         }
411 
412         if (widthMode == MeasureSpec.EXACTLY) {
413             width = myWidth;
414         }
415 
416         if (heightMode == MeasureSpec.EXACTLY) {
417             height = myHeight;
418         }
419 
420         mHasBaselineAlignedChild = false;
421 
422         View ignore = null;
423         int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
424         final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
425         gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
426         final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
427 
428         int left = Integer.MAX_VALUE;
429         int top = Integer.MAX_VALUE;
430         int right = Integer.MIN_VALUE;
431         int bottom = Integer.MIN_VALUE;
432 
433         boolean offsetHorizontalAxis = false;
434         boolean offsetVerticalAxis = false;
435 
436         if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
437             ignore = findViewById(mIgnoreGravity);
438         }
439 
440         final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
441         final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
442 
443         // We need to know our size for doing the correct computation of children positioning in RTL
444         // mode but there is no practical way to get it instead of running the code below.
445         // So, instead of running the code twice, we just set the width to a "default display width"
446         // before the computation and then, as a last pass, we will update their real position with
447         // an offset equals to "DEFAULT_WIDTH - width".
448         final int layoutDirection = getLayoutDirection();
449         if (isLayoutRtl() && myWidth == -1) {
450             myWidth = DEFAULT_WIDTH;
451         }
452 
453         View[] views = mSortedHorizontalChildren;
454         int count = views.length;
455 
456         for (int i = 0; i < count; i++) {
457             View child = views[i];
458             if (child.getVisibility() != GONE) {
459                 LayoutParams params = (LayoutParams) child.getLayoutParams();
460                 int[] rules = params.getRules(layoutDirection);
461 
462                 applyHorizontalSizeRules(params, myWidth, rules);
463                 measureChildHorizontal(child, params, myWidth, myHeight);
464 
465                 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
466                     offsetHorizontalAxis = true;
467                 }
468             }
469         }
470 
471         views = mSortedVerticalChildren;
472         count = views.length;
473         final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
474 
475         for (int i = 0; i < count; i++) {
476             View child = views[i];
477             if (child.getVisibility() != GONE) {
478                 LayoutParams params = (LayoutParams) child.getLayoutParams();
479 
480                 applyVerticalSizeRules(params, myHeight);
481                 measureChild(child, params, myWidth, myHeight);
482                 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
483                     offsetVerticalAxis = true;
484                 }
485 
486                 if (isWrapContentWidth) {
487                     if (isLayoutRtl()) {
488                         if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
489                             width = Math.max(width, myWidth - params.mLeft);
490                         } else {
491                             width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
492                         }
493                     } else {
494                         if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
495                             width = Math.max(width, params.mRight);
496                         } else {
497                             width = Math.max(width, params.mRight + params.rightMargin);
498                         }
499                     }
500                 }
501 
502                 if (isWrapContentHeight) {
503                     if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
504                         height = Math.max(height, params.mBottom);
505                     } else {
506                         height = Math.max(height, params.mBottom + params.bottomMargin);
507                     }
508                 }
509 
510                 if (child != ignore || verticalGravity) {
511                     left = Math.min(left, params.mLeft - params.leftMargin);
512                     top = Math.min(top, params.mTop - params.topMargin);
513                 }
514 
515                 if (child != ignore || horizontalGravity) {
516                     right = Math.max(right, params.mRight + params.rightMargin);
517                     bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
518                 }
519             }
520         }
521 
522         if (mHasBaselineAlignedChild) {
523             for (int i = 0; i < count; i++) {
524                 View child = getChildAt(i);
525                 if (child.getVisibility() != GONE) {
526                     LayoutParams params = (LayoutParams) child.getLayoutParams();
527                     alignBaseline(child, params);
528 
529                     if (child != ignore || verticalGravity) {
530                         left = Math.min(left, params.mLeft - params.leftMargin);
531                         top = Math.min(top, params.mTop - params.topMargin);
532                     }
533 
534                     if (child != ignore || horizontalGravity) {
535                         right = Math.max(right, params.mRight + params.rightMargin);
536                         bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
537                     }
538                 }
539             }
540         }
541 
542         if (isWrapContentWidth) {
543             // Width already has left padding in it since it was calculated by looking at
544             // the right of each child view
545             width += mPaddingRight;
546 
547             if (mLayoutParams != null && mLayoutParams.width >= 0) {
548                 width = Math.max(width, mLayoutParams.width);
549             }
550 
551             width = Math.max(width, getSuggestedMinimumWidth());
552             width = resolveSize(width, widthMeasureSpec);
553 
554             if (offsetHorizontalAxis) {
555                 for (int i = 0; i < count; i++) {
556                     View child = getChildAt(i);
557                     if (child.getVisibility() != GONE) {
558                         LayoutParams params = (LayoutParams) child.getLayoutParams();
559                         final int[] rules = params.getRules(layoutDirection);
560                         if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
561                             centerHorizontal(child, params, width);
562                         } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
563                             final int childWidth = child.getMeasuredWidth();
564                             params.mLeft = width - mPaddingRight - childWidth;
565                             params.mRight = params.mLeft + childWidth;
566                         }
567                     }
568                 }
569             }
570         }
571 
572         if (isWrapContentHeight) {
573             // Height already has top padding in it since it was calculated by looking at
574             // the bottom of each child view
575             height += mPaddingBottom;
576 
577             if (mLayoutParams != null && mLayoutParams.height >= 0) {
578                 height = Math.max(height, mLayoutParams.height);
579             }
580 
581             height = Math.max(height, getSuggestedMinimumHeight());
582             height = resolveSize(height, heightMeasureSpec);
583 
584             if (offsetVerticalAxis) {
585                 for (int i = 0; i < count; i++) {
586                     View child = getChildAt(i);
587                     if (child.getVisibility() != GONE) {
588                         LayoutParams params = (LayoutParams) child.getLayoutParams();
589                         final int[] rules = params.getRules(layoutDirection);
590                         if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
591                             centerVertical(child, params, height);
592                         } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
593                             final int childHeight = child.getMeasuredHeight();
594                             params.mTop = height - mPaddingBottom - childHeight;
595                             params.mBottom = params.mTop + childHeight;
596                         }
597                     }
598                 }
599             }
600         }
601 
602         if (horizontalGravity || verticalGravity) {
603             final Rect selfBounds = mSelfBounds;
604             selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
605                     height - mPaddingBottom);
606 
607             final Rect contentBounds = mContentBounds;
608             Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
609                     layoutDirection);
610 
611             final int horizontalOffset = contentBounds.left - left;
612             final int verticalOffset = contentBounds.top - top;
613             if (horizontalOffset != 0 || verticalOffset != 0) {
614                 for (int i = 0; i < count; i++) {
615                     View child = getChildAt(i);
616                     if (child.getVisibility() != GONE && child != ignore) {
617                         LayoutParams params = (LayoutParams) child.getLayoutParams();
618                         if (horizontalGravity) {
619                             params.mLeft += horizontalOffset;
620                             params.mRight += horizontalOffset;
621                         }
622                         if (verticalGravity) {
623                             params.mTop += verticalOffset;
624                             params.mBottom += verticalOffset;
625                         }
626                     }
627                 }
628             }
629         }
630 
631         if (isLayoutRtl()) {
632             final int offsetWidth = myWidth - width;
633             for (int i = 0; i < count; i++) {
634                 View child = getChildAt(i);
635                 if (child.getVisibility() != GONE) {
636                     LayoutParams params = (LayoutParams) child.getLayoutParams();
637                     params.mLeft -= offsetWidth;
638                     params.mRight -= offsetWidth;
639                 }
640             }
641 
642         }
643 
644         setMeasuredDimension(width, height);
645     }
646 
alignBaseline(View child, LayoutParams params)647     private void alignBaseline(View child, LayoutParams params) {
648         final int layoutDirection = getLayoutDirection();
649         int[] rules = params.getRules(layoutDirection);
650         int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE);
651 
652         if (anchorBaseline != -1) {
653             LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE);
654             if (anchorParams != null) {
655                 int offset = anchorParams.mTop + anchorBaseline;
656                 int baseline = child.getBaseline();
657                 if (baseline != -1) {
658                     offset -= baseline;
659                 }
660                 int height = params.mBottom - params.mTop;
661                 params.mTop = offset;
662                 params.mBottom = params.mTop + height;
663             }
664         }
665 
666         if (mBaselineView == null) {
667             mBaselineView = child;
668         } else {
669             LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams();
670             if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) {
671                 mBaselineView = child;
672             }
673         }
674     }
675 
676     /**
677      * Measure a child. The child should have left, top, right and bottom information
678      * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
679      * that the view can extend up to the corresponding edge.
680      *
681      * @param child Child to measure
682      * @param params LayoutParams associated with child
683      * @param myWidth Width of the the RelativeLayout
684      * @param myHeight Height of the RelativeLayout
685      */
measureChild(View child, LayoutParams params, int myWidth, int myHeight)686     private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
687         int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
688                 params.mRight, params.width,
689                 params.leftMargin, params.rightMargin,
690                 mPaddingLeft, mPaddingRight,
691                 myWidth);
692         int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
693                 params.mBottom, params.height,
694                 params.topMargin, params.bottomMargin,
695                 mPaddingTop, mPaddingBottom,
696                 myHeight);
697         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
698     }
699 
measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight)700     private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
701         int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
702                 params.mRight, params.width,
703                 params.leftMargin, params.rightMargin,
704                 mPaddingLeft, mPaddingRight,
705                 myWidth);
706         int maxHeight = myHeight;
707         if (mMeasureVerticalWithPaddingMargin) {
708             maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom -
709                     params.topMargin - params.bottomMargin);
710         }
711         int childHeightMeasureSpec;
712         if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
713             if (params.height >= 0) {
714                 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
715                         params.height, MeasureSpec.EXACTLY);
716             } else {
717                 // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement
718                 // is code for, "we got an unspecified mode in the RelativeLayout's measurespec."
719                 // Carry it forward.
720                 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
721             }
722         } else if (params.width == LayoutParams.MATCH_PARENT) {
723             childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
724         } else {
725             childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
726         }
727         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
728     }
729 
730     /**
731      * Get a measure spec that accounts for all of the constraints on this view.
732      * This includes size constraints imposed by the RelativeLayout as well as
733      * the View's desired dimension.
734      *
735      * @param childStart The left or top field of the child's layout params
736      * @param childEnd The right or bottom field of the child's layout params
737      * @param childSize The child's desired size (the width or height field of
738      *        the child's layout params)
739      * @param startMargin The left or top margin
740      * @param endMargin The right or bottom margin
741      * @param startPadding mPaddingLeft or mPaddingTop
742      * @param endPadding mPaddingRight or mPaddingBottom
743      * @param mySize The width or height of this view (the RelativeLayout)
744      * @return MeasureSpec for the child
745      */
getChildMeasureSpec(int childStart, int childEnd, int childSize, int startMargin, int endMargin, int startPadding, int endPadding, int mySize)746     private int getChildMeasureSpec(int childStart, int childEnd,
747             int childSize, int startMargin, int endMargin, int startPadding,
748             int endPadding, int mySize) {
749         int childSpecMode = 0;
750         int childSpecSize = 0;
751 
752         // Negative values in a mySize value in RelativeLayout
753         // measurement is code for, "we got an unspecified mode in the
754         // RelativeLayout's measure spec."
755         if (mySize < 0 && !mAllowBrokenMeasureSpecs) {
756             if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
757                 // Constraints fixed both edges, so child has an exact size.
758                 childSpecSize = Math.max(0, childEnd - childStart);
759                 childSpecMode = MeasureSpec.EXACTLY;
760             } else if (childSize >= 0) {
761                 // The child specified an exact size.
762                 childSpecSize = childSize;
763                 childSpecMode = MeasureSpec.EXACTLY;
764             } else {
765                 // Allow the child to be whatever size it wants.
766                 childSpecSize = 0;
767                 childSpecMode = MeasureSpec.UNSPECIFIED;
768             }
769 
770             return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
771         }
772 
773         // Figure out start and end bounds.
774         int tempStart = childStart;
775         int tempEnd = childEnd;
776 
777         // If the view did not express a layout constraint for an edge, use
778         // view's margins and our padding
779         if (tempStart == VALUE_NOT_SET) {
780             tempStart = startPadding + startMargin;
781         }
782         if (tempEnd == VALUE_NOT_SET) {
783             tempEnd = mySize - endPadding - endMargin;
784         }
785 
786         // Figure out maximum size available to this view
787         int maxAvailable = tempEnd - tempStart;
788 
789         if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
790             // Constraints fixed both edges, so child must be an exact size
791             childSpecMode = MeasureSpec.EXACTLY;
792             childSpecSize = maxAvailable;
793         } else {
794             if (childSize >= 0) {
795                 // Child wanted an exact size. Give as much as possible
796                 childSpecMode = MeasureSpec.EXACTLY;
797 
798                 if (maxAvailable >= 0) {
799                     // We have a maxmum size in this dimension.
800                     childSpecSize = Math.min(maxAvailable, childSize);
801                 } else {
802                     // We can grow in this dimension.
803                     childSpecSize = childSize;
804                 }
805             } else if (childSize == LayoutParams.MATCH_PARENT) {
806                 // Child wanted to be as big as possible. Give all available
807                 // space
808                 childSpecMode = MeasureSpec.EXACTLY;
809                 childSpecSize = maxAvailable;
810             } else if (childSize == LayoutParams.WRAP_CONTENT) {
811                 // Child wants to wrap content. Use AT_MOST
812                 // to communicate available space if we know
813                 // our max size
814                 if (maxAvailable >= 0) {
815                     // We have a maximum size in this dimension.
816                     childSpecMode = MeasureSpec.AT_MOST;
817                     childSpecSize = maxAvailable;
818                 } else {
819                     // We can grow in this dimension. Child can be as big as it
820                     // wants
821                     childSpecMode = MeasureSpec.UNSPECIFIED;
822                     childSpecSize = 0;
823                 }
824             }
825         }
826 
827         return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
828     }
829 
positionChildHorizontal(View child, LayoutParams params, int myWidth, boolean wrapContent)830     private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
831             boolean wrapContent) {
832 
833         final int layoutDirection = getLayoutDirection();
834         int[] rules = params.getRules(layoutDirection);
835 
836         if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
837             // Right is fixed, but left varies
838             params.mLeft = params.mRight - child.getMeasuredWidth();
839         } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
840             // Left is fixed, but right varies
841             params.mRight = params.mLeft + child.getMeasuredWidth();
842         } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
843             // Both left and right vary
844             if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
845                 if (!wrapContent) {
846                     centerHorizontal(child, params, myWidth);
847                 } else {
848                     params.mLeft = mPaddingLeft + params.leftMargin;
849                     params.mRight = params.mLeft + child.getMeasuredWidth();
850                 }
851                 return true;
852             } else {
853                 // This is the default case. For RTL we start from the right and for LTR we start
854                 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
855                 if (isLayoutRtl()) {
856                     params.mRight = myWidth - mPaddingRight- params.rightMargin;
857                     params.mLeft = params.mRight - child.getMeasuredWidth();
858                 } else {
859                     params.mLeft = mPaddingLeft + params.leftMargin;
860                     params.mRight = params.mLeft + child.getMeasuredWidth();
861                 }
862             }
863         }
864         return rules[ALIGN_PARENT_END] != 0;
865     }
866 
positionChildVertical(View child, LayoutParams params, int myHeight, boolean wrapContent)867     private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
868             boolean wrapContent) {
869 
870         int[] rules = params.getRules();
871 
872         if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) {
873             // Bottom is fixed, but top varies
874             params.mTop = params.mBottom - child.getMeasuredHeight();
875         } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
876             // Top is fixed, but bottom varies
877             params.mBottom = params.mTop + child.getMeasuredHeight();
878         } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
879             // Both top and bottom vary
880             if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
881                 if (!wrapContent) {
882                     centerVertical(child, params, myHeight);
883                 } else {
884                     params.mTop = mPaddingTop + params.topMargin;
885                     params.mBottom = params.mTop + child.getMeasuredHeight();
886                 }
887                 return true;
888             } else {
889                 params.mTop = mPaddingTop + params.topMargin;
890                 params.mBottom = params.mTop + child.getMeasuredHeight();
891             }
892         }
893         return rules[ALIGN_PARENT_BOTTOM] != 0;
894     }
895 
applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules)896     private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
897         RelativeLayout.LayoutParams anchorParams;
898 
899         // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
900         // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
901         // wants to the right
902         // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
903         // wants to the left
904         // left=10, right=20 means the left and right ends are both fixed
905         childParams.mLeft = VALUE_NOT_SET;
906         childParams.mRight = VALUE_NOT_SET;
907 
908         anchorParams = getRelatedViewParams(rules, LEFT_OF);
909         if (anchorParams != null) {
910             childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
911                     childParams.rightMargin);
912         } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
913             if (myWidth >= 0) {
914                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
915             }
916         }
917 
918         anchorParams = getRelatedViewParams(rules, RIGHT_OF);
919         if (anchorParams != null) {
920             childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
921                     childParams.leftMargin);
922         } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
923             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
924         }
925 
926         anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
927         if (anchorParams != null) {
928             childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
929         } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
930             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
931         }
932 
933         anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
934         if (anchorParams != null) {
935             childParams.mRight = anchorParams.mRight - childParams.rightMargin;
936         } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
937             if (myWidth >= 0) {
938                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
939             }
940         }
941 
942         if (0 != rules[ALIGN_PARENT_LEFT]) {
943             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
944         }
945 
946         if (0 != rules[ALIGN_PARENT_RIGHT]) {
947             if (myWidth >= 0) {
948                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
949             }
950         }
951     }
952 
applyVerticalSizeRules(LayoutParams childParams, int myHeight)953     private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) {
954         int[] rules = childParams.getRules();
955         RelativeLayout.LayoutParams anchorParams;
956 
957         childParams.mTop = VALUE_NOT_SET;
958         childParams.mBottom = VALUE_NOT_SET;
959 
960         anchorParams = getRelatedViewParams(rules, ABOVE);
961         if (anchorParams != null) {
962             childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
963                     childParams.bottomMargin);
964         } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
965             if (myHeight >= 0) {
966                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
967             }
968         }
969 
970         anchorParams = getRelatedViewParams(rules, BELOW);
971         if (anchorParams != null) {
972             childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
973                     childParams.topMargin);
974         } else if (childParams.alignWithParent && rules[BELOW] != 0) {
975             childParams.mTop = mPaddingTop + childParams.topMargin;
976         }
977 
978         anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
979         if (anchorParams != null) {
980             childParams.mTop = anchorParams.mTop + childParams.topMargin;
981         } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
982             childParams.mTop = mPaddingTop + childParams.topMargin;
983         }
984 
985         anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
986         if (anchorParams != null) {
987             childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
988         } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
989             if (myHeight >= 0) {
990                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
991             }
992         }
993 
994         if (0 != rules[ALIGN_PARENT_TOP]) {
995             childParams.mTop = mPaddingTop + childParams.topMargin;
996         }
997 
998         if (0 != rules[ALIGN_PARENT_BOTTOM]) {
999             if (myHeight >= 0) {
1000                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
1001             }
1002         }
1003 
1004         if (rules[ALIGN_BASELINE] != 0) {
1005             mHasBaselineAlignedChild = true;
1006         }
1007     }
1008 
getRelatedView(int[] rules, int relation)1009     private View getRelatedView(int[] rules, int relation) {
1010         int id = rules[relation];
1011         if (id != 0) {
1012             DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
1013             if (node == null) return null;
1014             View v = node.view;
1015 
1016             // Find the first non-GONE view up the chain
1017             while (v.getVisibility() == View.GONE) {
1018                 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
1019                 node = mGraph.mKeyNodes.get((rules[relation]));
1020                 if (node == null) return null;
1021                 v = node.view;
1022             }
1023 
1024             return v;
1025         }
1026 
1027         return null;
1028     }
1029 
getRelatedViewParams(int[] rules, int relation)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 
getRelatedViewBaseline(int[] rules, int relation)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 
centerHorizontal(View child, LayoutParams params, int myWidth)1049     private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
1050         int childWidth = child.getMeasuredWidth();
1051         int left = (myWidth - childWidth) / 2;
1052 
1053         params.mLeft = left;
1054         params.mRight = left + childWidth;
1055     }
1056 
centerVertical(View child, LayoutParams params, int myHeight)1057     private static void centerVertical(View child, LayoutParams params, int myHeight) {
1058         int childHeight = child.getMeasuredHeight();
1059         int top = (myHeight - childHeight) / 2;
1060 
1061         params.mTop = top;
1062         params.mBottom = top + childHeight;
1063     }
1064 
1065     @Override
onLayout(boolean changed, int l, int t, int r, int b)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.
1069         final int count = getChildCount();
1070 
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);
1077             }
1078         }
1079     }
1080 
1081     @Override
generateLayoutParams(AttributeSet attrs)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
generateDefaultLayoutParams()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
checkLayoutParams(ViewGroup.LayoutParams p)1098     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1099         return p instanceof RelativeLayout.LayoutParams;
1100     }
1101 
1102     @Override
generateLayoutParams(ViewGroup.LayoutParams p)1103     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1104         return new LayoutParams(p);
1105     }
1106 
1107     @Override
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)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) {
1119             if (view.getVisibility() == View.VISIBLE
1120                     && view.dispatchPopulateAccessibilityEvent(event)) {
1121                 mTopToBottomLeftToRightSet.clear();
1122                 return true;
1123             }
1124         }
1125 
1126         mTopToBottomLeftToRightSet.clear();
1127         return false;
1128     }
1129 
1130     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)1131     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1132         super.onInitializeAccessibilityEvent(event);
1133         event.setClassName(RelativeLayout.class.getName());
1134     }
1135 
1136     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)1137     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1138         super.onInitializeAccessibilityNodeInfo(info);
1139         info.setClassName(RelativeLayout.class.getName());
1140     }
1141 
1142     /**
1143      * Compares two views in left-to-right and top-to-bottom fashion.
1144      */
1145      private class TopToBottomLeftToRightComparator implements Comparator<View> {
compare(View first, View second)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 
1171     /**
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
1191      * @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
1197      */
1198     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1199         @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
1200             @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"),
1215             @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")
1222         }, mapping = {
1223             @ViewDebug.IntToString(from = TRUE, to = "true"),
1224             @ViewDebug.IntToString(from = 0,    to = "false/NO_ID")
1225         })
1226 
1227         private int[] mRules = new int[VERB_COUNT];
1228         private int[] mInitialRules = new int[VERB_COUNT];
1229 
1230         private int mLeft, mTop, mRight, mBottom;
1231 
1232         private boolean mRulesChanged = false;
1233         private boolean mIsRtlCompatibilityMode = false;
1234 
1235         /**
1236          * When true, uses the parent as the anchor if the anchor doesn't exist or if
1237          * the anchor's visibility is GONE.
1238          */
1239         @ViewDebug.ExportedProperty(category = "layout")
1240         public boolean alignWithParent;
1241 
LayoutParams(Context c, AttributeSet attrs)1242         public LayoutParams(Context c, AttributeSet attrs) {
1243             super(c, attrs);
1244 
1245             TypedArray a = c.obtainStyledAttributes(attrs,
1246                     com.android.internal.R.styleable.RelativeLayout_Layout);
1247 
1248             final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
1249             mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
1250                     !c.getApplicationInfo().hasRtlSupport());
1251 
1252             final int[] rules = mRules;
1253             //noinspection MismatchedReadAndWriteOfArray
1254             final int[] initialRules = mInitialRules;
1255 
1256             final int N = a.getIndexCount();
1257             for (int i = 0; i < N; i++) {
1258                 int attr = a.getIndex(i);
1259                 switch (attr) {
1260                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1261                         alignWithParent = a.getBoolean(attr, false);
1262                         break;
1263                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1264                         rules[LEFT_OF] = a.getResourceId(attr, 0);
1265                         break;
1266                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1267                         rules[RIGHT_OF] = a.getResourceId(attr, 0);
1268                         break;
1269                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1270                         rules[ABOVE] = a.getResourceId(attr, 0);
1271                         break;
1272                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1273                         rules[BELOW] = a.getResourceId(attr, 0);
1274                         break;
1275                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1276                         rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1277                         break;
1278                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1279                         rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1280                         break;
1281                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1282                         rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1283                         break;
1284                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1285                         rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1286                         break;
1287                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1288                         rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1289                         break;
1290                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1291                         rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1292                         break;
1293                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1294                         rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1295                         break;
1296                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1297                         rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1298                         break;
1299                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1300                         rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1301                         break;
1302                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1303                         rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1304                         break;
1305                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1306                         rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1307                         break;
1308                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1309                         rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1310                        break;
1311                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1312                         rules[START_OF] = a.getResourceId(attr, 0);
1313                         break;
1314                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1315                         rules[END_OF] = a.getResourceId(attr, 0);
1316                         break;
1317                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1318                         rules[ALIGN_START] = a.getResourceId(attr, 0);
1319                         break;
1320                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1321                         rules[ALIGN_END] = a.getResourceId(attr, 0);
1322                         break;
1323                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1324                         rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1325                         break;
1326                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1327                         rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1328                         break;
1329                 }
1330             }
1331             mRulesChanged = true;
1332             System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
1333 
1334             a.recycle();
1335         }
1336 
LayoutParams(int w, int h)1337         public LayoutParams(int w, int h) {
1338             super(w, h);
1339         }
1340 
1341         /**
1342          * {@inheritDoc}
1343          */
LayoutParams(ViewGroup.LayoutParams source)1344         public LayoutParams(ViewGroup.LayoutParams source) {
1345             super(source);
1346         }
1347 
1348         /**
1349          * {@inheritDoc}
1350          */
LayoutParams(ViewGroup.MarginLayoutParams source)1351         public LayoutParams(ViewGroup.MarginLayoutParams source) {
1352             super(source);
1353         }
1354 
1355         /**
1356          * Copy constructor. Clones the width, height, margin values, and rules
1357          * of the source.
1358          *
1359          * @param source The layout params to copy from.
1360          */
LayoutParams(LayoutParams source)1361         public LayoutParams(LayoutParams source) {
1362             super(source);
1363 
1364             this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
1365             this.mRulesChanged = source.mRulesChanged;
1366             this.alignWithParent = source.alignWithParent;
1367 
1368             System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT);
1369             System.arraycopy(
1370                     source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT);
1371         }
1372 
1373         @Override
debug(String output)1374         public String debug(String output) {
1375             return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1376                     ", height=" + sizeToString(height) + " }";
1377         }
1378 
1379         /**
1380          * Adds a layout rule to be interpreted by the RelativeLayout. This
1381          * method should only be used for constraints that don't refer to another sibling
1382          * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE}
1383          * for true or 0 for false). To specify a verb that takes a subject, use
1384          * {@link #addRule(int, int)} instead.
1385          *
1386          * @param verb One of the verbs defined by
1387          *        {@link android.widget.RelativeLayout RelativeLayout}, such as
1388          *        ALIGN_WITH_PARENT_LEFT.
1389          * @see #addRule(int, int)
1390          */
addRule(int verb)1391         public void addRule(int verb) {
1392             mRules[verb] = TRUE;
1393             mInitialRules[verb] = TRUE;
1394             mRulesChanged = true;
1395         }
1396 
1397         /**
1398          * Adds a layout rule to be interpreted by the RelativeLayout. Use this for
1399          * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
1400          * value (VISIBLE).
1401          *
1402          * @param verb One of the verbs defined by
1403          *        {@link android.widget.RelativeLayout RelativeLayout}, such as
1404          *         ALIGN_WITH_PARENT_LEFT.
1405          * @param anchor The id of another view to use as an anchor,
1406          *        or a boolean value (represented as {@link RelativeLayout#TRUE}
1407          *        for true or 0 for false).  For verbs that don't refer to another sibling
1408          *        (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
1409          * @see #addRule(int)
1410          */
addRule(int verb, int anchor)1411         public void addRule(int verb, int anchor) {
1412             mRules[verb] = anchor;
1413             mInitialRules[verb] = anchor;
1414             mRulesChanged = true;
1415         }
1416 
1417         /**
1418          * Removes a layout rule to be interpreted by the RelativeLayout.
1419          *
1420          * @param verb One of the verbs defined by
1421          *        {@link android.widget.RelativeLayout RelativeLayout}, such as
1422          *         ALIGN_WITH_PARENT_LEFT.
1423          * @see #addRule(int)
1424          * @see #addRule(int, int)
1425          */
removeRule(int verb)1426         public void removeRule(int verb) {
1427             mRules[verb] = 0;
1428             mInitialRules[verb] = 0;
1429             mRulesChanged = true;
1430         }
1431 
hasRelativeRules()1432         private boolean hasRelativeRules() {
1433             return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1434                     mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1435                     mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1436         }
1437 
1438         // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
1439         // or not.
1440         //
1441         // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
1442         // predominance over any "start/end" rules that could have been defined. A special case:
1443         // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
1444         // resolve those "start"/"end" rules to "left"/"right" respectively.
1445         //
1446         // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
1447         // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
1448         //
1449         // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
1450         // only the "left"/"right" rules at the end.
resolveRules(int layoutDirection)1451         private void resolveRules(int layoutDirection) {
1452             final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
1453 
1454             // Reset to initial state
1455             System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
1456 
1457             // Apply rules depending on direction and if we are in RTL compatibility mode
1458             if (mIsRtlCompatibilityMode) {
1459                 if (mRules[ALIGN_START] != 0) {
1460                     if (mRules[ALIGN_LEFT] == 0) {
1461                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1462                         // the "left" rule
1463                         mRules[ALIGN_LEFT] = mRules[ALIGN_START];
1464                     }
1465                     mRules[ALIGN_START] = 0;
1466                 }
1467 
1468                 if (mRules[ALIGN_END] != 0) {
1469                     if (mRules[ALIGN_RIGHT] == 0) {
1470                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1471                         // "right" rule
1472                         mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
1473                     }
1474                     mRules[ALIGN_END] = 0;
1475                 }
1476 
1477                 if (mRules[START_OF] != 0) {
1478                     if (mRules[LEFT_OF] == 0) {
1479                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1480                         // the "left" rule
1481                         mRules[LEFT_OF] = mRules[START_OF];
1482                     }
1483                     mRules[START_OF] = 0;
1484                 }
1485 
1486                 if (mRules[END_OF] != 0) {
1487                     if (mRules[RIGHT_OF] == 0) {
1488                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1489                         // "right" rule
1490                         mRules[RIGHT_OF] = mRules[END_OF];
1491                     }
1492                     mRules[END_OF] = 0;
1493                 }
1494 
1495                 if (mRules[ALIGN_PARENT_START] != 0) {
1496                     if (mRules[ALIGN_PARENT_LEFT] == 0) {
1497                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1498                         // the "left" rule
1499                         mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1500                     }
1501                     mRules[ALIGN_PARENT_START] = 0;
1502                 }
1503 
1504                 if (mRules[ALIGN_PARENT_END] != 0) {
1505                     if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1506                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1507                         // "right" rule
1508                         mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1509                     }
1510                     mRules[ALIGN_PARENT_END] = 0;
1511                 }
1512             } else {
1513                 // JB MR1+ case
1514                 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
1515                         (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
1516                     // "start"/"end" rules take precedence over "left"/"right" rules
1517                     mRules[ALIGN_LEFT] = 0;
1518                     mRules[ALIGN_RIGHT] = 0;
1519                 }
1520                 if (mRules[ALIGN_START] != 0) {
1521                     // "start" rule resolved to "left" or "right" depending on the direction
1522                     mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1523                     mRules[ALIGN_START] = 0;
1524                 }
1525                 if (mRules[ALIGN_END] != 0) {
1526                     // "end" rule resolved to "left" or "right" depending on the direction
1527                     mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1528                     mRules[ALIGN_END] = 0;
1529                 }
1530 
1531                 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
1532                         (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
1533                     // "start"/"end" rules take precedence over "left"/"right" rules
1534                     mRules[LEFT_OF] = 0;
1535                     mRules[RIGHT_OF] = 0;
1536                 }
1537                 if (mRules[START_OF] != 0) {
1538                     // "start" rule resolved to "left" or "right" depending on the direction
1539                     mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1540                     mRules[START_OF] = 0;
1541                 }
1542                 if (mRules[END_OF] != 0) {
1543                     // "end" rule resolved to "left" or "right" depending on the direction
1544                     mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1545                     mRules[END_OF] = 0;
1546                 }
1547 
1548                 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
1549                         (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
1550                     // "start"/"end" rules take precedence over "left"/"right" rules
1551                     mRules[ALIGN_PARENT_LEFT] = 0;
1552                     mRules[ALIGN_PARENT_RIGHT] = 0;
1553                 }
1554                 if (mRules[ALIGN_PARENT_START] != 0) {
1555                     // "start" rule resolved to "left" or "right" depending on the direction
1556                     mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1557                     mRules[ALIGN_PARENT_START] = 0;
1558                 }
1559                 if (mRules[ALIGN_PARENT_END] != 0) {
1560                     // "end" rule resolved to "left" or "right" depending on the direction
1561                     mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1562                     mRules[ALIGN_PARENT_END] = 0;
1563                 }
1564             }
1565             mRulesChanged = false;
1566         }
1567 
1568         /**
1569          * Retrieves a complete list of all supported rules, where the index is the rule
1570          * verb, and the element value is the value specified, or "false" if it was never
1571          * set. If there are relative rules defined (*_START / *_END), they will be resolved
1572          * depending on the layout direction.
1573          *
1574          * @param layoutDirection the direction of the layout.
1575          *                        Should be either {@link View#LAYOUT_DIRECTION_LTR}
1576          *                        or {@link View#LAYOUT_DIRECTION_RTL}
1577          * @return the supported rules
1578          * @see #addRule(int, int)
1579          *
1580          * @hide
1581          */
getRules(int layoutDirection)1582         public int[] getRules(int layoutDirection) {
1583             if (hasRelativeRules() &&
1584                     (mRulesChanged || layoutDirection != getLayoutDirection())) {
1585                 resolveRules(layoutDirection);
1586                 if (layoutDirection != getLayoutDirection()) {
1587                     setLayoutDirection(layoutDirection);
1588                 }
1589             }
1590             return mRules;
1591         }
1592 
1593         /**
1594          * Retrieves a complete list of all supported rules, where the index is the rule
1595          * verb, and the element value is the value specified, or "false" if it was never
1596          * set. There will be no resolution of relative rules done.
1597          *
1598          * @return the supported rules
1599          * @see #addRule(int, int)
1600          */
getRules()1601         public int[] getRules() {
1602             return mRules;
1603         }
1604 
1605         @Override
resolveLayoutDirection(int layoutDirection)1606         public void resolveLayoutDirection(int layoutDirection) {
1607             final boolean isLayoutRtl = isLayoutRtl();
1608             if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
1609                 resolveRules(layoutDirection);
1610             }
1611             // This will set the layout direction
1612             super.resolveLayoutDirection(layoutDirection);
1613         }
1614     }
1615 
1616     private static class DependencyGraph {
1617         /**
1618          * List of all views in the graph.
1619          */
1620         private ArrayList<Node> mNodes = new ArrayList<Node>();
1621 
1622         /**
1623          * List of nodes in the graph. Each node is identified by its
1624          * view id (see View#getId()).
1625          */
1626         private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
1627 
1628         /**
1629          * Temporary data structure used to build the list of roots
1630          * for this graph.
1631          */
1632         private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
1633 
1634         /**
1635          * Clears the graph.
1636          */
clear()1637         void clear() {
1638             final ArrayList<Node> nodes = mNodes;
1639             final int count = nodes.size();
1640 
1641             for (int i = 0; i < count; i++) {
1642                 nodes.get(i).release();
1643             }
1644             nodes.clear();
1645 
1646             mKeyNodes.clear();
1647             mRoots.clear();
1648         }
1649 
1650         /**
1651          * Adds a view to the graph.
1652          *
1653          * @param view The view to be added as a node to the graph.
1654          */
add(View view)1655         void add(View view) {
1656             final int id = view.getId();
1657             final Node node = Node.acquire(view);
1658 
1659             if (id != View.NO_ID) {
1660                 mKeyNodes.put(id, node);
1661             }
1662 
1663             mNodes.add(node);
1664         }
1665 
1666         /**
1667          * Builds a sorted list of views. The sorting order depends on the dependencies
1668          * between the view. For instance, if view C needs view A to be processed first
1669          * and view A needs view B to be processed first, the dependency graph
1670          * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1671          *
1672          * @param sorted The sorted list of views. The length of this array must
1673          *        be equal to getChildCount().
1674          * @param rules The list of rules to take into account.
1675          */
getSortedViews(View[] sorted, int... rules)1676         void getSortedViews(View[] sorted, int... rules) {
1677             final ArrayDeque<Node> roots = findRoots(rules);
1678             int index = 0;
1679 
1680             Node node;
1681             while ((node = roots.pollLast()) != null) {
1682                 final View view = node.view;
1683                 final int key = view.getId();
1684 
1685                 sorted[index++] = view;
1686 
1687                 final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
1688                 final int count = dependents.size();
1689                 for (int i = 0; i < count; i++) {
1690                     final Node dependent = dependents.keyAt(i);
1691                     final SparseArray<Node> dependencies = dependent.dependencies;
1692 
1693                     dependencies.remove(key);
1694                     if (dependencies.size() == 0) {
1695                         roots.add(dependent);
1696                     }
1697                 }
1698             }
1699 
1700             if (index < sorted.length) {
1701                 throw new IllegalStateException("Circular dependencies cannot exist"
1702                         + " in RelativeLayout");
1703             }
1704         }
1705 
1706         /**
1707          * Finds the roots of the graph. A root is a node with no dependency and
1708          * with [0..n] dependents.
1709          *
1710          * @param rulesFilter The list of rules to consider when building the
1711          *        dependencies
1712          *
1713          * @return A list of node, each being a root of the graph
1714          */
findRoots(int[] rulesFilter)1715         private ArrayDeque<Node> findRoots(int[] rulesFilter) {
1716             final SparseArray<Node> keyNodes = mKeyNodes;
1717             final ArrayList<Node> nodes = mNodes;
1718             final int count = nodes.size();
1719 
1720             // Find roots can be invoked several times, so make sure to clear
1721             // all dependents and dependencies before running the algorithm
1722             for (int i = 0; i < count; i++) {
1723                 final Node node = nodes.get(i);
1724                 node.dependents.clear();
1725                 node.dependencies.clear();
1726             }
1727 
1728             // Builds up the dependents and dependencies for each node of the graph
1729             for (int i = 0; i < count; i++) {
1730                 final Node node = nodes.get(i);
1731 
1732                 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1733                 final int[] rules = layoutParams.mRules;
1734                 final int rulesCount = rulesFilter.length;
1735 
1736                 // Look only the the rules passed in parameter, this way we build only the
1737                 // dependencies for a specific set of rules
1738                 for (int j = 0; j < rulesCount; j++) {
1739                     final int rule = rules[rulesFilter[j]];
1740                     if (rule > 0) {
1741                         // The node this node depends on
1742                         final Node dependency = keyNodes.get(rule);
1743                         // Skip unknowns and self dependencies
1744                         if (dependency == null || dependency == node) {
1745                             continue;
1746                         }
1747                         // Add the current node as a dependent
1748                         dependency.dependents.put(node, this);
1749                         // Add a dependency to the current node
1750                         node.dependencies.put(rule, dependency);
1751                     }
1752                 }
1753             }
1754 
1755             final ArrayDeque<Node> roots = mRoots;
1756             roots.clear();
1757 
1758             // Finds all the roots in the graph: all nodes with no dependencies
1759             for (int i = 0; i < count; i++) {
1760                 final Node node = nodes.get(i);
1761                 if (node.dependencies.size() == 0) roots.addLast(node);
1762             }
1763 
1764             return roots;
1765         }
1766 
1767         /**
1768          * A node in the dependency graph. A node is a view, its list of dependencies
1769          * and its list of dependents.
1770          *
1771          * A node with no dependent is considered a root of the graph.
1772          */
1773         static class Node {
1774             /**
1775              * The view representing this node in the layout.
1776              */
1777             View view;
1778 
1779             /**
1780              * The list of dependents for this node; a dependent is a node
1781              * that needs this node to be processed first.
1782              */
1783             final ArrayMap<Node, DependencyGraph> dependents =
1784                     new ArrayMap<Node, DependencyGraph>();
1785 
1786             /**
1787              * The list of dependencies for this node.
1788              */
1789             final SparseArray<Node> dependencies = new SparseArray<Node>();
1790 
1791             /*
1792              * START POOL IMPLEMENTATION
1793              */
1794             // The pool is static, so all nodes instances are shared across
1795             // activities, that's why we give it a rather high limit
1796             private static final int POOL_LIMIT = 100;
1797             private static final SynchronizedPool<Node> sPool =
1798                     new SynchronizedPool<Node>(POOL_LIMIT);
1799 
acquire(View view)1800             static Node acquire(View view) {
1801                 Node node = sPool.acquire();
1802                 if (node == null) {
1803                     node = new Node();
1804                 }
1805                 node.view = view;
1806                 return node;
1807             }
1808 
release()1809             void release() {
1810                 view = null;
1811                 dependents.clear();
1812                 dependencies.clear();
1813 
1814                 sPool.release(this);
1815             }
1816             /*
1817              * END POOL IMPLEMENTATION
1818              */
1819         }
1820     }
1821 }
1822