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