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