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