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