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