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