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