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