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