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