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