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