RelativeLayout.java revision 8f8a11b7fa26e603519131001ab46596aa30ba1a
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    /**
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
235    public RelativeLayout(Context context) {
236        this(context, null);
237    }
238
239    public RelativeLayout(Context context, AttributeSet attrs) {
240        this(context, attrs, 0);
241    }
242
243    public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
244        this(context, attrs, defStyleAttr, 0);
245    }
246
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
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
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
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
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     */
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
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
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
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
354    public int getBaseline() {
355        return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
356    }
357
358    @Override
359    public void requestLayout() {
360        super.requestLayout();
361        mDirtyHierarchy = true;
362    }
363
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
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
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     */
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
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     */
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
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
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
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
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
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
1030    private LayoutParams getRelatedViewParams(int[] rules, int relation) {
1031        View v = getRelatedView(rules, relation);
1032        if (v != null) {
1033            ViewGroup.LayoutParams params = v.getLayoutParams();
1034            if (params instanceof LayoutParams) {
1035                return (LayoutParams) v.getLayoutParams();
1036            }
1037        }
1038        return null;
1039    }
1040
1041    private int getRelatedViewBaseline(int[] rules, int relation) {
1042        View v = getRelatedView(rules, relation);
1043        if (v != null) {
1044            return v.getBaseline();
1045        }
1046        return -1;
1047    }
1048
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
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
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
1082    public LayoutParams generateLayoutParams(AttributeSet attrs) {
1083        return new RelativeLayout.LayoutParams(getContext(), attrs);
1084    }
1085
1086    /**
1087     * Returns a set of layout parameters with a width of
1088     * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1089     * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1090     */
1091    @Override
1092    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1093        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1094    }
1095
1096    // Override to allow type-checking of LayoutParams.
1097    @Override
1098    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1099        return p instanceof RelativeLayout.LayoutParams;
1100    }
1101
1102    @Override
1103    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1104        return new LayoutParams(p);
1105    }
1106
1107    @Override
1108    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1109        if (mTopToBottomLeftToRightSet == null) {
1110            mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1111        }
1112
1113        // sort children top-to-bottom and left-to-right
1114        for (int i = 0, count = getChildCount(); i < count; i++) {
1115            mTopToBottomLeftToRightSet.add(getChildAt(i));
1116        }
1117
1118        for (View view : mTopToBottomLeftToRightSet) {
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
1131    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1132        super.onInitializeAccessibilityEvent(event);
1133        event.setClassName(RelativeLayout.class.getName());
1134    }
1135
1136    @Override
1137    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1138        super.onInitializeAccessibilityNodeInfo(info);
1139        info.setClassName(RelativeLayout.class.getName());
1140    }
1141
1142    /**
1143     * Compares two views in left-to-right and top-to-bottom fashion.
1144     */
1145     private class TopToBottomLeftToRightComparator implements Comparator<View> {
1146        public int compare(View first, View second) {
1147            // top - bottom
1148            int topDifference = first.getTop() - second.getTop();
1149            if (topDifference != 0) {
1150                return topDifference;
1151            }
1152            // left - right
1153            int leftDifference = first.getLeft() - second.getLeft();
1154            if (leftDifference != 0) {
1155                return leftDifference;
1156            }
1157            // break tie by height
1158            int heightDiference = first.getHeight() - second.getHeight();
1159            if (heightDiference != 0) {
1160                return heightDiference;
1161            }
1162            // break tie by width
1163            int widthDiference = first.getWidth() - second.getWidth();
1164            if (widthDiference != 0) {
1165                return widthDiference;
1166            }
1167            return 0;
1168        }
1169    }
1170
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
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
1337        public LayoutParams(int w, int h) {
1338            super(w, h);
1339        }
1340
1341        /**
1342         * {@inheritDoc}
1343         */
1344        public LayoutParams(ViewGroup.LayoutParams source) {
1345            super(source);
1346        }
1347
1348        /**
1349         * {@inheritDoc}
1350         */
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         */
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
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         */
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         */
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         */
1426        public void removeRule(int verb) {
1427            mRules[verb] = 0;
1428            mInitialRules[verb] = 0;
1429            mRulesChanged = true;
1430        }
1431
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.
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         */
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         */
1601        public int[] getRules() {
1602            return mRules;
1603        }
1604
1605        @Override
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         */
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         */
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         */
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         */
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
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
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