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