• 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 static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
20 
21 import android.annotation.NonNull;
22 import android.content.Context;
23 import android.content.res.TypedArray;
24 import android.graphics.Rect;
25 import android.os.Build;
26 import android.util.ArrayMap;
27 import android.util.AttributeSet;
28 import android.util.Pools.SynchronizedPool;
29 import android.util.SparseArray;
30 import android.view.Gravity;
31 import android.view.View;
32 import android.view.ViewDebug;
33 import android.view.ViewGroup;
34 import android.view.ViewHierarchyEncoder;
35 import android.view.accessibility.AccessibilityEvent;
36 import android.widget.RemoteViews.RemoteView;
37 
38 import com.android.internal.R;
39 
40 import java.util.ArrayDeque;
41 import java.util.ArrayList;
42 import java.util.Comparator;
43 import java.util.SortedSet;
44 import java.util.TreeSet;
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                     positionAtEdge(child, params, myWidth);
837                 }
838                 return true;
839             } else {
840                 // This is the default case. For RTL we start from the right and for LTR we start
841                 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
842                 positionAtEdge(child, params, myWidth);
843             }
844         }
845         return rules[ALIGN_PARENT_END] != 0;
846     }
847 
positionAtEdge(View child, LayoutParams params, int myWidth)848     private void positionAtEdge(View child, LayoutParams params, int myWidth) {
849         if (isLayoutRtl()) {
850             params.mRight = myWidth - mPaddingRight - params.rightMargin;
851             params.mLeft = params.mRight - child.getMeasuredWidth();
852         } else {
853             params.mLeft = mPaddingLeft + params.leftMargin;
854             params.mRight = params.mLeft + child.getMeasuredWidth();
855         }
856     }
857 
positionChildVertical(View child, LayoutParams params, int myHeight, boolean wrapContent)858     private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
859             boolean wrapContent) {
860 
861         int[] rules = params.getRules();
862 
863         if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) {
864             // Bottom is fixed, but top varies
865             params.mTop = params.mBottom - child.getMeasuredHeight();
866         } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
867             // Top is fixed, but bottom varies
868             params.mBottom = params.mTop + child.getMeasuredHeight();
869         } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
870             // Both top and bottom vary
871             if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
872                 if (!wrapContent) {
873                     centerVertical(child, params, myHeight);
874                 } else {
875                     params.mTop = mPaddingTop + params.topMargin;
876                     params.mBottom = params.mTop + child.getMeasuredHeight();
877                 }
878                 return true;
879             } else {
880                 params.mTop = mPaddingTop + params.topMargin;
881                 params.mBottom = params.mTop + child.getMeasuredHeight();
882             }
883         }
884         return rules[ALIGN_PARENT_BOTTOM] != 0;
885     }
886 
applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules)887     private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
888         RelativeLayout.LayoutParams anchorParams;
889 
890         // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
891         // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
892         // wants to the right
893         // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
894         // wants to the left
895         // left=10, right=20 means the left and right ends are both fixed
896         childParams.mLeft = VALUE_NOT_SET;
897         childParams.mRight = VALUE_NOT_SET;
898 
899         anchorParams = getRelatedViewParams(rules, LEFT_OF);
900         if (anchorParams != null) {
901             childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
902                     childParams.rightMargin);
903         } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
904             if (myWidth >= 0) {
905                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
906             }
907         }
908 
909         anchorParams = getRelatedViewParams(rules, RIGHT_OF);
910         if (anchorParams != null) {
911             childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
912                     childParams.leftMargin);
913         } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
914             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
915         }
916 
917         anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
918         if (anchorParams != null) {
919             childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
920         } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
921             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
922         }
923 
924         anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
925         if (anchorParams != null) {
926             childParams.mRight = anchorParams.mRight - childParams.rightMargin;
927         } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
928             if (myWidth >= 0) {
929                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
930             }
931         }
932 
933         if (0 != rules[ALIGN_PARENT_LEFT]) {
934             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
935         }
936 
937         if (0 != rules[ALIGN_PARENT_RIGHT]) {
938             if (myWidth >= 0) {
939                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
940             }
941         }
942     }
943 
applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline)944     private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) {
945         final int[] rules = childParams.getRules();
946 
947         // Baseline alignment overrides any explicitly specified top or bottom.
948         int baselineOffset = getRelatedViewBaselineOffset(rules);
949         if (baselineOffset != -1) {
950             if (myBaseline != -1) {
951                 baselineOffset -= myBaseline;
952             }
953             childParams.mTop = baselineOffset;
954             childParams.mBottom = VALUE_NOT_SET;
955             return;
956         }
957 
958         RelativeLayout.LayoutParams anchorParams;
959 
960         childParams.mTop = VALUE_NOT_SET;
961         childParams.mBottom = VALUE_NOT_SET;
962 
963         anchorParams = getRelatedViewParams(rules, ABOVE);
964         if (anchorParams != null) {
965             childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
966                     childParams.bottomMargin);
967         } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
968             if (myHeight >= 0) {
969                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
970             }
971         }
972 
973         anchorParams = getRelatedViewParams(rules, BELOW);
974         if (anchorParams != null) {
975             childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
976                     childParams.topMargin);
977         } else if (childParams.alignWithParent && rules[BELOW] != 0) {
978             childParams.mTop = mPaddingTop + childParams.topMargin;
979         }
980 
981         anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
982         if (anchorParams != null) {
983             childParams.mTop = anchorParams.mTop + childParams.topMargin;
984         } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
985             childParams.mTop = mPaddingTop + childParams.topMargin;
986         }
987 
988         anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
989         if (anchorParams != null) {
990             childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
991         } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
992             if (myHeight >= 0) {
993                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
994             }
995         }
996 
997         if (0 != rules[ALIGN_PARENT_TOP]) {
998             childParams.mTop = mPaddingTop + childParams.topMargin;
999         }
1000 
1001         if (0 != rules[ALIGN_PARENT_BOTTOM]) {
1002             if (myHeight >= 0) {
1003                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
1004             }
1005         }
1006     }
1007 
getRelatedView(int[] rules, int relation)1008     private View getRelatedView(int[] rules, int relation) {
1009         int id = rules[relation];
1010         if (id != 0) {
1011             DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
1012             if (node == null) return null;
1013             View v = node.view;
1014 
1015             // Find the first non-GONE view up the chain
1016             while (v.getVisibility() == View.GONE) {
1017                 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
1018                 node = mGraph.mKeyNodes.get((rules[relation]));
1019                 // ignore self dependency. for more info look in git commit: da3003
1020                 if (node == null || v == node.view) return null;
1021                 v = node.view;
1022             }
1023 
1024             return v;
1025         }
1026 
1027         return null;
1028     }
1029 
getRelatedViewParams(int[] rules, int relation)1030     private LayoutParams getRelatedViewParams(int[] rules, int relation) {
1031         View v = getRelatedView(rules, relation);
1032         if (v != null) {
1033             ViewGroup.LayoutParams params = v.getLayoutParams();
1034             if (params instanceof LayoutParams) {
1035                 return (LayoutParams) v.getLayoutParams();
1036             }
1037         }
1038         return null;
1039     }
1040 
getRelatedViewBaselineOffset(int[] rules)1041     private int getRelatedViewBaselineOffset(int[] rules) {
1042         final View v = getRelatedView(rules, ALIGN_BASELINE);
1043         if (v != null) {
1044             final int baseline = v.getBaseline();
1045             if (baseline != -1) {
1046                 final ViewGroup.LayoutParams params = v.getLayoutParams();
1047                 if (params instanceof LayoutParams) {
1048                     final LayoutParams anchorParams = (LayoutParams) v.getLayoutParams();
1049                     return anchorParams.mTop + baseline;
1050                 }
1051             }
1052         }
1053         return -1;
1054     }
1055 
centerHorizontal(View child, LayoutParams params, int myWidth)1056     private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
1057         int childWidth = child.getMeasuredWidth();
1058         int left = (myWidth - childWidth) / 2;
1059 
1060         params.mLeft = left;
1061         params.mRight = left + childWidth;
1062     }
1063 
centerVertical(View child, LayoutParams params, int myHeight)1064     private static void centerVertical(View child, LayoutParams params, int myHeight) {
1065         int childHeight = child.getMeasuredHeight();
1066         int top = (myHeight - childHeight) / 2;
1067 
1068         params.mTop = top;
1069         params.mBottom = top + childHeight;
1070     }
1071 
1072     @Override
onLayout(boolean changed, int l, int t, int r, int b)1073     protected void onLayout(boolean changed, int l, int t, int r, int b) {
1074         //  The layout has actually already been performed and the positions
1075         //  cached.  Apply the cached values to the children.
1076         final int count = getChildCount();
1077 
1078         for (int i = 0; i < count; i++) {
1079             View child = getChildAt(i);
1080             if (child.getVisibility() != GONE) {
1081                 RelativeLayout.LayoutParams st =
1082                         (RelativeLayout.LayoutParams) child.getLayoutParams();
1083                 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
1084             }
1085         }
1086     }
1087 
1088     @Override
generateLayoutParams(AttributeSet attrs)1089     public LayoutParams generateLayoutParams(AttributeSet attrs) {
1090         return new RelativeLayout.LayoutParams(getContext(), attrs);
1091     }
1092 
1093     /**
1094      * Returns a set of layout parameters with a width of
1095      * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1096      * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1097      */
1098     @Override
generateDefaultLayoutParams()1099     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1100         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1101     }
1102 
1103     // Override to allow type-checking of LayoutParams.
1104     @Override
checkLayoutParams(ViewGroup.LayoutParams p)1105     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1106         return p instanceof RelativeLayout.LayoutParams;
1107     }
1108 
1109     @Override
generateLayoutParams(ViewGroup.LayoutParams lp)1110     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
1111         if (sPreserveMarginParamsInLayoutParamConversion) {
1112             if (lp instanceof LayoutParams) {
1113                 return new LayoutParams((LayoutParams) lp);
1114             } else if (lp instanceof MarginLayoutParams) {
1115                 return new LayoutParams((MarginLayoutParams) lp);
1116             }
1117         }
1118         return new LayoutParams(lp);
1119     }
1120 
1121     /** @hide */
1122     @Override
dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)1123     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
1124         if (mTopToBottomLeftToRightSet == null) {
1125             mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1126         }
1127 
1128         // sort children top-to-bottom and left-to-right
1129         for (int i = 0, count = getChildCount(); i < count; i++) {
1130             mTopToBottomLeftToRightSet.add(getChildAt(i));
1131         }
1132 
1133         for (View view : mTopToBottomLeftToRightSet) {
1134             if (view.getVisibility() == View.VISIBLE
1135                     && view.dispatchPopulateAccessibilityEvent(event)) {
1136                 mTopToBottomLeftToRightSet.clear();
1137                 return true;
1138             }
1139         }
1140 
1141         mTopToBottomLeftToRightSet.clear();
1142         return false;
1143     }
1144 
1145     @Override
getAccessibilityClassName()1146     public CharSequence getAccessibilityClassName() {
1147         return RelativeLayout.class.getName();
1148     }
1149 
1150     /**
1151      * Compares two views in left-to-right and top-to-bottom fashion.
1152      */
1153      private class TopToBottomLeftToRightComparator implements Comparator<View> {
compare(View first, View second)1154         public int compare(View first, View second) {
1155             // top - bottom
1156             int topDifference = first.getTop() - second.getTop();
1157             if (topDifference != 0) {
1158                 return topDifference;
1159             }
1160             // left - right
1161             int leftDifference = first.getLeft() - second.getLeft();
1162             if (leftDifference != 0) {
1163                 return leftDifference;
1164             }
1165             // break tie by height
1166             int heightDiference = first.getHeight() - second.getHeight();
1167             if (heightDiference != 0) {
1168                 return heightDiference;
1169             }
1170             // break tie by width
1171             int widthDiference = first.getWidth() - second.getWidth();
1172             if (widthDiference != 0) {
1173                 return widthDiference;
1174             }
1175             return 0;
1176         }
1177     }
1178 
1179     /**
1180      * Specifies how a view is positioned within a {@link RelativeLayout}.
1181      * The relative layout containing the view uses the value of these layout parameters to
1182      * determine where to position the view on the screen.  If the view is not contained
1183      * within a relative layout, these attributes are ignored.
1184      *
1185      * See the <a href=“https://developer.android.com/guide/topics/ui/layout/relative.html”>
1186      * Relative Layout</a> guide for example code demonstrating how to use relative layout’s
1187      * layout parameters in a layout XML.
1188      *
1189      * To learn more about layout parameters and how they differ from typical view attributes,
1190      * see the <a href=“https://developer.android.com/guide/topics/ui/declaring-layout.html#attributes”>
1191      *     Layouts guide</a>.
1192      *
1193      *
1194      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1195      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1196      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1197      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1198      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1199      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1200      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1201      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1202      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1203      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1204      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1205      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1206      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1207      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1208      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1209      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1210      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
1211      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1212      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1213      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1214      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1215      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1216      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
1217      */
1218     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1219         @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
1220             @ViewDebug.IntToString(from = ABOVE,               to = "above"),
1221             @ViewDebug.IntToString(from = ALIGN_BASELINE,      to = "alignBaseline"),
1222             @ViewDebug.IntToString(from = ALIGN_BOTTOM,        to = "alignBottom"),
1223             @ViewDebug.IntToString(from = ALIGN_LEFT,          to = "alignLeft"),
1224             @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1225             @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT,   to = "alignParentLeft"),
1226             @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT,  to = "alignParentRight"),
1227             @ViewDebug.IntToString(from = ALIGN_PARENT_TOP,    to = "alignParentTop"),
1228             @ViewDebug.IntToString(from = ALIGN_RIGHT,         to = "alignRight"),
1229             @ViewDebug.IntToString(from = ALIGN_TOP,           to = "alignTop"),
1230             @ViewDebug.IntToString(from = BELOW,               to = "below"),
1231             @ViewDebug.IntToString(from = CENTER_HORIZONTAL,   to = "centerHorizontal"),
1232             @ViewDebug.IntToString(from = CENTER_IN_PARENT,    to = "center"),
1233             @ViewDebug.IntToString(from = CENTER_VERTICAL,     to = "centerVertical"),
1234             @ViewDebug.IntToString(from = LEFT_OF,             to = "leftOf"),
1235             @ViewDebug.IntToString(from = RIGHT_OF,            to = "rightOf"),
1236             @ViewDebug.IntToString(from = ALIGN_START,         to = "alignStart"),
1237             @ViewDebug.IntToString(from = ALIGN_END,           to = "alignEnd"),
1238             @ViewDebug.IntToString(from = ALIGN_PARENT_START,  to = "alignParentStart"),
1239             @ViewDebug.IntToString(from = ALIGN_PARENT_END,    to = "alignParentEnd"),
1240             @ViewDebug.IntToString(from = START_OF,            to = "startOf"),
1241             @ViewDebug.IntToString(from = END_OF,              to = "endOf")
1242         }, mapping = {
1243             @ViewDebug.IntToString(from = TRUE, to = "true"),
1244             @ViewDebug.IntToString(from = 0,    to = "false/NO_ID")
1245         })
1246 
1247         private int[] mRules = new int[VERB_COUNT];
1248         private int[] mInitialRules = new int[VERB_COUNT];
1249 
1250         private int mLeft, mTop, mRight, mBottom;
1251 
1252         /**
1253          * Whether this view had any relative rules modified following the most
1254          * recent resolution of layout direction.
1255          */
1256         private boolean mNeedsLayoutResolution;
1257 
1258         private boolean mRulesChanged = false;
1259         private boolean mIsRtlCompatibilityMode = false;
1260 
1261         /**
1262          * When true, uses the parent as the anchor if the anchor doesn't exist or if
1263          * the anchor's visibility is GONE.
1264          */
1265         @ViewDebug.ExportedProperty(category = "layout")
1266         public boolean alignWithParent;
1267 
LayoutParams(Context c, AttributeSet attrs)1268         public LayoutParams(Context c, AttributeSet attrs) {
1269             super(c, attrs);
1270 
1271             TypedArray a = c.obtainStyledAttributes(attrs,
1272                     com.android.internal.R.styleable.RelativeLayout_Layout);
1273 
1274             final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
1275             mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
1276                     !c.getApplicationInfo().hasRtlSupport());
1277 
1278             final int[] rules = mRules;
1279             //noinspection MismatchedReadAndWriteOfArray
1280             final int[] initialRules = mInitialRules;
1281 
1282             final int N = a.getIndexCount();
1283             for (int i = 0; i < N; i++) {
1284                 int attr = a.getIndex(i);
1285                 switch (attr) {
1286                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1287                         alignWithParent = a.getBoolean(attr, false);
1288                         break;
1289                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1290                         rules[LEFT_OF] = a.getResourceId(attr, 0);
1291                         break;
1292                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1293                         rules[RIGHT_OF] = a.getResourceId(attr, 0);
1294                         break;
1295                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1296                         rules[ABOVE] = a.getResourceId(attr, 0);
1297                         break;
1298                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1299                         rules[BELOW] = a.getResourceId(attr, 0);
1300                         break;
1301                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1302                         rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1303                         break;
1304                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1305                         rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1306                         break;
1307                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1308                         rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1309                         break;
1310                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1311                         rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1312                         break;
1313                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1314                         rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1315                         break;
1316                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1317                         rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1318                         break;
1319                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1320                         rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1321                         break;
1322                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1323                         rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1324                         break;
1325                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1326                         rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1327                         break;
1328                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1329                         rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1330                         break;
1331                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1332                         rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1333                         break;
1334                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1335                         rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1336                        break;
1337                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1338                         rules[START_OF] = a.getResourceId(attr, 0);
1339                         break;
1340                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1341                         rules[END_OF] = a.getResourceId(attr, 0);
1342                         break;
1343                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1344                         rules[ALIGN_START] = a.getResourceId(attr, 0);
1345                         break;
1346                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1347                         rules[ALIGN_END] = a.getResourceId(attr, 0);
1348                         break;
1349                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1350                         rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1351                         break;
1352                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1353                         rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1354                         break;
1355                 }
1356             }
1357             mRulesChanged = true;
1358             System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
1359 
1360             a.recycle();
1361         }
1362 
LayoutParams(int w, int h)1363         public LayoutParams(int w, int h) {
1364             super(w, h);
1365         }
1366 
1367         /**
1368          * {@inheritDoc}
1369          */
LayoutParams(ViewGroup.LayoutParams source)1370         public LayoutParams(ViewGroup.LayoutParams source) {
1371             super(source);
1372         }
1373 
1374         /**
1375          * {@inheritDoc}
1376          */
LayoutParams(ViewGroup.MarginLayoutParams source)1377         public LayoutParams(ViewGroup.MarginLayoutParams source) {
1378             super(source);
1379         }
1380 
1381         /**
1382          * Copy constructor. Clones the width, height, margin values, and rules
1383          * of the source.
1384          *
1385          * @param source The layout params to copy from.
1386          */
LayoutParams(LayoutParams source)1387         public LayoutParams(LayoutParams source) {
1388             super(source);
1389 
1390             this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
1391             this.mRulesChanged = source.mRulesChanged;
1392             this.alignWithParent = source.alignWithParent;
1393 
1394             System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT);
1395             System.arraycopy(
1396                     source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT);
1397         }
1398 
1399         @Override
debug(String output)1400         public String debug(String output) {
1401             return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1402                     ", height=" + sizeToString(height) + " }";
1403         }
1404 
1405         /**
1406          * Adds a layout rule to be interpreted by the RelativeLayout.
1407          * <p>
1408          * This method should only be used for verbs that don't refer to a
1409          * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean
1410          * value ({@link #TRUE} for true or 0 for false). To
1411          * specify a verb that takes a subject, use {@link #addRule(int, int)}.
1412          * <p>
1413          * If the rule is relative to the layout direction (ex.
1414          * {@link #ALIGN_PARENT_START}), then the layout direction must be
1415          * resolved using {@link #resolveLayoutDirection(int)} before calling
1416          * {@link #getRule(int)} an absolute rule (ex.
1417          * {@link #ALIGN_PARENT_LEFT}.
1418          *
1419          * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT}
1420          * @see #addRule(int, int)
1421          * @see #removeRule(int)
1422          * @see #getRule(int)
1423          */
addRule(int verb)1424         public void addRule(int verb) {
1425             addRule(verb, TRUE);
1426         }
1427 
1428         /**
1429          * Adds a layout rule to be interpreted by the RelativeLayout.
1430          * <p>
1431          * Use this for verbs that refer to a sibling (ex.
1432          * {@link #ALIGN_RIGHT}) or take a boolean value (ex.
1433          * {@link #CENTER_IN_PARENT}).
1434          * <p>
1435          * If the rule is relative to the layout direction (ex.
1436          * {@link #START_OF}), then the layout direction must be resolved using
1437          * {@link #resolveLayoutDirection(int)} before calling
1438          * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
1439          *
1440          * @param verb a layout verb, such as {@link #ALIGN_RIGHT}
1441          * @param subject the ID of another view to use as an anchor, or a
1442          *                boolean value (represented as {@link #TRUE} for true
1443          *                or 0 for false)
1444          * @see #addRule(int)
1445          * @see #removeRule(int)
1446          * @see #getRule(int)
1447          */
addRule(int verb, int subject)1448         public void addRule(int verb, int subject) {
1449             // If we're removing a relative rule, we'll need to force layout
1450             // resolution the next time it's requested.
1451             if (!mNeedsLayoutResolution && isRelativeRule(verb)
1452                     && mInitialRules[verb] != 0 && subject == 0) {
1453                 mNeedsLayoutResolution = true;
1454             }
1455 
1456             mRules[verb] = subject;
1457             mInitialRules[verb] = subject;
1458             mRulesChanged = true;
1459         }
1460 
1461         /**
1462          * Removes a layout rule to be interpreted by the RelativeLayout.
1463          * <p>
1464          * If the rule is relative to the layout direction (ex.
1465          * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the
1466          * layout direction must be resolved using
1467          * {@link #resolveLayoutDirection(int)} before before calling
1468          * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
1469          *
1470          * @param verb One of the verbs defined by
1471          *        {@link android.widget.RelativeLayout RelativeLayout}, such as
1472          *         ALIGN_WITH_PARENT_LEFT.
1473          * @see #addRule(int)
1474          * @see #addRule(int, int)
1475          * @see #getRule(int)
1476          */
removeRule(int verb)1477         public void removeRule(int verb) {
1478             addRule(verb, 0);
1479         }
1480 
1481         /**
1482          * Returns the layout rule associated with a specific verb.
1483          *
1484          * @param verb one of the verbs defined by {@link RelativeLayout}, such
1485          *             as ALIGN_WITH_PARENT_LEFT
1486          * @return the id of another view to use as an anchor, a boolean value
1487          *         (represented as {@link RelativeLayout#TRUE} for true
1488          *         or 0 for false), or -1 for verbs that don't refer to another
1489          *         sibling (for example, ALIGN_WITH_PARENT_BOTTOM)
1490          * @see #addRule(int)
1491          * @see #addRule(int, int)
1492          */
getRule(int verb)1493         public int getRule(int verb) {
1494             return mRules[verb];
1495         }
1496 
hasRelativeRules()1497         private boolean hasRelativeRules() {
1498             return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1499                     mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1500                     mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1501         }
1502 
isRelativeRule(int rule)1503         private boolean isRelativeRule(int rule) {
1504             return rule == START_OF || rule == END_OF
1505                     || rule == ALIGN_START || rule == ALIGN_END
1506                     || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END;
1507         }
1508 
1509         // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
1510         // or not.
1511         //
1512         // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
1513         // predominance over any "start/end" rules that could have been defined. A special case:
1514         // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
1515         // resolve those "start"/"end" rules to "left"/"right" respectively.
1516         //
1517         // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
1518         // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
1519         //
1520         // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
1521         // only the "left"/"right" rules at the end.
resolveRules(int layoutDirection)1522         private void resolveRules(int layoutDirection) {
1523             final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
1524 
1525             // Reset to initial state
1526             System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
1527 
1528             // Apply rules depending on direction and if we are in RTL compatibility mode
1529             if (mIsRtlCompatibilityMode) {
1530                 if (mRules[ALIGN_START] != 0) {
1531                     if (mRules[ALIGN_LEFT] == 0) {
1532                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1533                         // the "left" rule
1534                         mRules[ALIGN_LEFT] = mRules[ALIGN_START];
1535                     }
1536                     mRules[ALIGN_START] = 0;
1537                 }
1538 
1539                 if (mRules[ALIGN_END] != 0) {
1540                     if (mRules[ALIGN_RIGHT] == 0) {
1541                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1542                         // "right" rule
1543                         mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
1544                     }
1545                     mRules[ALIGN_END] = 0;
1546                 }
1547 
1548                 if (mRules[START_OF] != 0) {
1549                     if (mRules[LEFT_OF] == 0) {
1550                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1551                         // the "left" rule
1552                         mRules[LEFT_OF] = mRules[START_OF];
1553                     }
1554                     mRules[START_OF] = 0;
1555                 }
1556 
1557                 if (mRules[END_OF] != 0) {
1558                     if (mRules[RIGHT_OF] == 0) {
1559                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1560                         // "right" rule
1561                         mRules[RIGHT_OF] = mRules[END_OF];
1562                     }
1563                     mRules[END_OF] = 0;
1564                 }
1565 
1566                 if (mRules[ALIGN_PARENT_START] != 0) {
1567                     if (mRules[ALIGN_PARENT_LEFT] == 0) {
1568                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1569                         // the "left" rule
1570                         mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1571                     }
1572                     mRules[ALIGN_PARENT_START] = 0;
1573                 }
1574 
1575                 if (mRules[ALIGN_PARENT_END] != 0) {
1576                     if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1577                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1578                         // "right" rule
1579                         mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1580                     }
1581                     mRules[ALIGN_PARENT_END] = 0;
1582                 }
1583             } else {
1584                 // JB MR1+ case
1585                 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
1586                         (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
1587                     // "start"/"end" rules take precedence over "left"/"right" rules
1588                     mRules[ALIGN_LEFT] = 0;
1589                     mRules[ALIGN_RIGHT] = 0;
1590                 }
1591                 if (mRules[ALIGN_START] != 0) {
1592                     // "start" rule resolved to "left" or "right" depending on the direction
1593                     mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1594                     mRules[ALIGN_START] = 0;
1595                 }
1596                 if (mRules[ALIGN_END] != 0) {
1597                     // "end" rule resolved to "left" or "right" depending on the direction
1598                     mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1599                     mRules[ALIGN_END] = 0;
1600                 }
1601 
1602                 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
1603                         (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
1604                     // "start"/"end" rules take precedence over "left"/"right" rules
1605                     mRules[LEFT_OF] = 0;
1606                     mRules[RIGHT_OF] = 0;
1607                 }
1608                 if (mRules[START_OF] != 0) {
1609                     // "start" rule resolved to "left" or "right" depending on the direction
1610                     mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1611                     mRules[START_OF] = 0;
1612                 }
1613                 if (mRules[END_OF] != 0) {
1614                     // "end" rule resolved to "left" or "right" depending on the direction
1615                     mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1616                     mRules[END_OF] = 0;
1617                 }
1618 
1619                 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
1620                         (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
1621                     // "start"/"end" rules take precedence over "left"/"right" rules
1622                     mRules[ALIGN_PARENT_LEFT] = 0;
1623                     mRules[ALIGN_PARENT_RIGHT] = 0;
1624                 }
1625                 if (mRules[ALIGN_PARENT_START] != 0) {
1626                     // "start" rule resolved to "left" or "right" depending on the direction
1627                     mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1628                     mRules[ALIGN_PARENT_START] = 0;
1629                 }
1630                 if (mRules[ALIGN_PARENT_END] != 0) {
1631                     // "end" rule resolved to "left" or "right" depending on the direction
1632                     mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1633                     mRules[ALIGN_PARENT_END] = 0;
1634                 }
1635             }
1636 
1637             mRulesChanged = false;
1638             mNeedsLayoutResolution = false;
1639         }
1640 
1641         /**
1642          * Retrieves a complete list of all supported rules, where the index is the rule
1643          * verb, and the element value is the value specified, or "false" if it was never
1644          * set. If there are relative rules defined (*_START / *_END), they will be resolved
1645          * depending on the layout direction.
1646          *
1647          * @param layoutDirection the direction of the layout.
1648          *                        Should be either {@link View#LAYOUT_DIRECTION_LTR}
1649          *                        or {@link View#LAYOUT_DIRECTION_RTL}
1650          * @return the supported rules
1651          * @see #addRule(int, int)
1652          *
1653          * @hide
1654          */
getRules(int layoutDirection)1655         public int[] getRules(int layoutDirection) {
1656             resolveLayoutDirection(layoutDirection);
1657             return mRules;
1658         }
1659 
1660         /**
1661          * Retrieves a complete list of all supported rules, where the index is the rule
1662          * verb, and the element value is the value specified, or "false" if it was never
1663          * set. There will be no resolution of relative rules done.
1664          *
1665          * @return the supported rules
1666          * @see #addRule(int, int)
1667          */
getRules()1668         public int[] getRules() {
1669             return mRules;
1670         }
1671 
1672         /**
1673          * This will be called by {@link android.view.View#requestLayout()} to
1674          * resolve layout parameters that are relative to the layout direction.
1675          * <p>
1676          * After this method is called, any rules using layout-relative verbs
1677          * (ex. {@link #START_OF}) previously added via {@link #addRule(int)}
1678          * may only be accessed via their resolved absolute verbs (ex.
1679          * {@link #LEFT_OF}).
1680          */
1681         @Override
resolveLayoutDirection(int layoutDirection)1682         public void resolveLayoutDirection(int layoutDirection) {
1683             if (shouldResolveLayoutDirection(layoutDirection)) {
1684                 resolveRules(layoutDirection);
1685             }
1686 
1687             // This will set the layout direction.
1688             super.resolveLayoutDirection(layoutDirection);
1689         }
1690 
shouldResolveLayoutDirection(int layoutDirection)1691         private boolean shouldResolveLayoutDirection(int layoutDirection) {
1692             return (mNeedsLayoutResolution || hasRelativeRules())
1693                     && (mRulesChanged || layoutDirection != getLayoutDirection());
1694         }
1695 
1696         /** @hide */
1697         @Override
encodeProperties(@onNull ViewHierarchyEncoder encoder)1698         protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
1699             super.encodeProperties(encoder);
1700             encoder.addProperty("layout:alignWithParent", alignWithParent);
1701         }
1702     }
1703 
1704     private static class DependencyGraph {
1705         /**
1706          * List of all views in the graph.
1707          */
1708         private ArrayList<Node> mNodes = new ArrayList<Node>();
1709 
1710         /**
1711          * List of nodes in the graph. Each node is identified by its
1712          * view id (see View#getId()).
1713          */
1714         private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
1715 
1716         /**
1717          * Temporary data structure used to build the list of roots
1718          * for this graph.
1719          */
1720         private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
1721 
1722         /**
1723          * Clears the graph.
1724          */
clear()1725         void clear() {
1726             final ArrayList<Node> nodes = mNodes;
1727             final int count = nodes.size();
1728 
1729             for (int i = 0; i < count; i++) {
1730                 nodes.get(i).release();
1731             }
1732             nodes.clear();
1733 
1734             mKeyNodes.clear();
1735             mRoots.clear();
1736         }
1737 
1738         /**
1739          * Adds a view to the graph.
1740          *
1741          * @param view The view to be added as a node to the graph.
1742          */
add(View view)1743         void add(View view) {
1744             final int id = view.getId();
1745             final Node node = Node.acquire(view);
1746 
1747             if (id != View.NO_ID) {
1748                 mKeyNodes.put(id, node);
1749             }
1750 
1751             mNodes.add(node);
1752         }
1753 
1754         /**
1755          * Builds a sorted list of views. The sorting order depends on the dependencies
1756          * between the view. For instance, if view C needs view A to be processed first
1757          * and view A needs view B to be processed first, the dependency graph
1758          * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1759          *
1760          * @param sorted The sorted list of views. The length of this array must
1761          *        be equal to getChildCount().
1762          * @param rules The list of rules to take into account.
1763          */
getSortedViews(View[] sorted, int... rules)1764         void getSortedViews(View[] sorted, int... rules) {
1765             final ArrayDeque<Node> roots = findRoots(rules);
1766             int index = 0;
1767 
1768             Node node;
1769             while ((node = roots.pollLast()) != null) {
1770                 final View view = node.view;
1771                 final int key = view.getId();
1772 
1773                 sorted[index++] = view;
1774 
1775                 final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
1776                 final int count = dependents.size();
1777                 for (int i = 0; i < count; i++) {
1778                     final Node dependent = dependents.keyAt(i);
1779                     final SparseArray<Node> dependencies = dependent.dependencies;
1780 
1781                     dependencies.remove(key);
1782                     if (dependencies.size() == 0) {
1783                         roots.add(dependent);
1784                     }
1785                 }
1786             }
1787 
1788             if (index < sorted.length) {
1789                 throw new IllegalStateException("Circular dependencies cannot exist"
1790                         + " in RelativeLayout");
1791             }
1792         }
1793 
1794         /**
1795          * Finds the roots of the graph. A root is a node with no dependency and
1796          * with [0..n] dependents.
1797          *
1798          * @param rulesFilter The list of rules to consider when building the
1799          *        dependencies
1800          *
1801          * @return A list of node, each being a root of the graph
1802          */
findRoots(int[] rulesFilter)1803         private ArrayDeque<Node> findRoots(int[] rulesFilter) {
1804             final SparseArray<Node> keyNodes = mKeyNodes;
1805             final ArrayList<Node> nodes = mNodes;
1806             final int count = nodes.size();
1807 
1808             // Find roots can be invoked several times, so make sure to clear
1809             // all dependents and dependencies before running the algorithm
1810             for (int i = 0; i < count; i++) {
1811                 final Node node = nodes.get(i);
1812                 node.dependents.clear();
1813                 node.dependencies.clear();
1814             }
1815 
1816             // Builds up the dependents and dependencies for each node of the graph
1817             for (int i = 0; i < count; i++) {
1818                 final Node node = nodes.get(i);
1819 
1820                 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1821                 final int[] rules = layoutParams.mRules;
1822                 final int rulesCount = rulesFilter.length;
1823 
1824                 // Look only the the rules passed in parameter, this way we build only the
1825                 // dependencies for a specific set of rules
1826                 for (int j = 0; j < rulesCount; j++) {
1827                     final int rule = rules[rulesFilter[j]];
1828                     if (rule > 0) {
1829                         // The node this node depends on
1830                         final Node dependency = keyNodes.get(rule);
1831                         // Skip unknowns and self dependencies
1832                         if (dependency == null || dependency == node) {
1833                             continue;
1834                         }
1835                         // Add the current node as a dependent
1836                         dependency.dependents.put(node, this);
1837                         // Add a dependency to the current node
1838                         node.dependencies.put(rule, dependency);
1839                     }
1840                 }
1841             }
1842 
1843             final ArrayDeque<Node> roots = mRoots;
1844             roots.clear();
1845 
1846             // Finds all the roots in the graph: all nodes with no dependencies
1847             for (int i = 0; i < count; i++) {
1848                 final Node node = nodes.get(i);
1849                 if (node.dependencies.size() == 0) roots.addLast(node);
1850             }
1851 
1852             return roots;
1853         }
1854 
1855         /**
1856          * A node in the dependency graph. A node is a view, its list of dependencies
1857          * and its list of dependents.
1858          *
1859          * A node with no dependent is considered a root of the graph.
1860          */
1861         static class Node {
1862             /**
1863              * The view representing this node in the layout.
1864              */
1865             View view;
1866 
1867             /**
1868              * The list of dependents for this node; a dependent is a node
1869              * that needs this node to be processed first.
1870              */
1871             final ArrayMap<Node, DependencyGraph> dependents =
1872                     new ArrayMap<Node, DependencyGraph>();
1873 
1874             /**
1875              * The list of dependencies for this node.
1876              */
1877             final SparseArray<Node> dependencies = new SparseArray<Node>();
1878 
1879             /*
1880              * START POOL IMPLEMENTATION
1881              */
1882             // The pool is static, so all nodes instances are shared across
1883             // activities, that's why we give it a rather high limit
1884             private static final int POOL_LIMIT = 100;
1885             private static final SynchronizedPool<Node> sPool =
1886                     new SynchronizedPool<Node>(POOL_LIMIT);
1887 
acquire(View view)1888             static Node acquire(View view) {
1889                 Node node = sPool.acquire();
1890                 if (node == null) {
1891                     node = new Node();
1892                 }
1893                 node.view = view;
1894                 return node;
1895             }
1896 
release()1897             void release() {
1898                 view = null;
1899                 dependents.clear();
1900                 dependencies.clear();
1901 
1902                 sPool.release(this);
1903             }
1904             /*
1905              * END POOL IMPLEMENTATION
1906              */
1907         }
1908     }
1909 }
1910