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