OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / widget / RelativeLayout.java
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 }