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