/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.widget;

import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;

import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Build;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Pools.SynchronizedPool;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.widget.RemoteViews.RemoteView;

import com.android.internal.R;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;

A Layout where the positions of the children can be described in relation to each other or to the parent.

Note that you cannot have a circular dependency between the size of the RelativeLayout and the position of its children. For example, you cannot have a RelativeLayout whose height is set to WRAP_CONTENT and a child set to ALIGN_PARENT_BOTTOM.

Note: In platform version 17 and lower, RelativeLayout was affected by a measurement bug that could cause child views to be measured with incorrect MeasureSpec values. (See MeasureSpec.makeMeasureSpec for more details.) This was triggered when a RelativeLayout container was placed in a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view not equipped to properly measure with the MeasureSpec mode UNSPECIFIED was placed in a RelativeLayout, this would silently work anyway as RelativeLayout would pass a very large AT_MOST MeasureSpec instead.

This behavior has been preserved for apps that set android:targetSdkVersion="17" or older in their manifest's uses-sdk tag for compatibility. Apps targeting SDK version 18 or newer will receive the correct behavior

See the Relative Layout guide.

Also see RelativeLayout.LayoutParams for layout attributes

@attrref android.R.styleable#RelativeLayout_gravity
@attrref android.R.styleable#RelativeLayout_ignoreGravity
/** * A Layout where the positions of the children can be described in relation to each other or to the * parent. * * <p> * Note that you cannot have a circular dependency between the size of the RelativeLayout and the * position of its children. For example, you cannot have a RelativeLayout whose height is set to * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to * {@link #ALIGN_PARENT_BOTTOM}. * </p> * * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by * a measurement bug that could cause child views to be measured with incorrect * {@link android.view.View.MeasureSpec MeasureSpec} values. (See * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec} * for more details.) This was triggered when a RelativeLayout container was placed in * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view * not equipped to properly measure with the MeasureSpec mode * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout, * this would silently work anyway as RelativeLayout would pass a very large * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p> * * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code> * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK * version 18 or newer will receive the correct behavior</p> * * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative * Layout</a> guide.</p> * * <p> * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for * layout attributes * </p> * * @attr ref android.R.styleable#RelativeLayout_gravity * @attr ref android.R.styleable#RelativeLayout_ignoreGravity */
@RemoteView public class RelativeLayout extends ViewGroup { public static final int TRUE = -1;
Rule that aligns a child's right edge with another child's left edge.
/** * Rule that aligns a child's right edge with another child's left edge. */
public static final int LEFT_OF = 0;
Rule that aligns a child's left edge with another child's right edge.
/** * Rule that aligns a child's left edge with another child's right edge. */
public static final int RIGHT_OF = 1;
Rule that aligns a child's bottom edge with another child's top edge.
/** * Rule that aligns a child's bottom edge with another child's top edge. */
public static final int ABOVE = 2;
Rule that aligns a child's top edge with another child's bottom edge.
/** * Rule that aligns a child's top edge with another child's bottom edge. */
public static final int BELOW = 3;
Rule that aligns a child's baseline with another child's baseline.
/** * Rule that aligns a child's baseline with another child's baseline. */
public static final int ALIGN_BASELINE = 4;
Rule that aligns a child's left edge with another child's left edge.
/** * Rule that aligns a child's left edge with another child's left edge. */
public static final int ALIGN_LEFT = 5;
Rule that aligns a child's top edge with another child's top edge.
/** * Rule that aligns a child's top edge with another child's top edge. */
public static final int ALIGN_TOP = 6;
Rule that aligns a child's right edge with another child's right edge.
/** * Rule that aligns a child's right edge with another child's right edge. */
public static final int ALIGN_RIGHT = 7;
Rule that aligns a child's bottom edge with another child's bottom edge.
/** * Rule that aligns a child's bottom edge with another child's bottom edge. */
public static final int ALIGN_BOTTOM = 8;
Rule that aligns the child's left edge with its RelativeLayout parent's left edge.
/** * Rule that aligns the child's left edge with its RelativeLayout * parent's left edge. */
public static final int ALIGN_PARENT_LEFT = 9;
Rule that aligns the child's top edge with its RelativeLayout parent's top edge.
/** * Rule that aligns the child's top edge with its RelativeLayout * parent's top edge. */
public static final int ALIGN_PARENT_TOP = 10;
Rule that aligns the child's right edge with its RelativeLayout parent's right edge.
/** * Rule that aligns the child's right edge with its RelativeLayout * parent's right edge. */
public static final int ALIGN_PARENT_RIGHT = 11;
Rule that aligns the child's bottom edge with its RelativeLayout parent's bottom edge.
/** * Rule that aligns the child's bottom edge with its RelativeLayout * parent's bottom edge. */
public static final int ALIGN_PARENT_BOTTOM = 12;
Rule that centers the child with respect to the bounds of its RelativeLayout parent.
/** * Rule that centers the child with respect to the bounds of its * RelativeLayout parent. */
public static final int CENTER_IN_PARENT = 13;
Rule that centers the child horizontally with respect to the bounds of its RelativeLayout parent.
/** * Rule that centers the child horizontally with respect to the * bounds of its RelativeLayout parent. */
public static final int CENTER_HORIZONTAL = 14;
Rule that centers the child vertically with respect to the bounds of its RelativeLayout parent.
/** * Rule that centers the child vertically with respect to the * bounds of its RelativeLayout parent. */
public static final int CENTER_VERTICAL = 15;
Rule that aligns a child's end edge with another child's start edge.
/** * Rule that aligns a child's end edge with another child's start edge. */
public static final int START_OF = 16;
Rule that aligns a child's start edge with another child's end edge.
/** * Rule that aligns a child's start edge with another child's end edge. */
public static final int END_OF = 17;
Rule that aligns a child's start edge with another child's start edge.
/** * Rule that aligns a child's start edge with another child's start edge. */
public static final int ALIGN_START = 18;
Rule that aligns a child's end edge with another child's end edge.
/** * Rule that aligns a child's end edge with another child's end edge. */
public static final int ALIGN_END = 19;
Rule that aligns the child's start edge with its RelativeLayout parent's start edge.
/** * Rule that aligns the child's start edge with its RelativeLayout * parent's start edge. */
public static final int ALIGN_PARENT_START = 20;
Rule that aligns the child's end edge with its RelativeLayout parent's end edge.
/** * Rule that aligns the child's end edge with its RelativeLayout * parent's end edge. */
public static final int ALIGN_PARENT_END = 21; private static final int VERB_COUNT = 22; private static final int[] RULES_VERTICAL = { ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM }; private static final int[] RULES_HORIZONTAL = { LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END };
Used to indicate left/right/top/bottom should be inferred from constraints
/** * Used to indicate left/right/top/bottom should be inferred from constraints */
private static final int VALUE_NOT_SET = Integer.MIN_VALUE; private View mBaselineView = null; private int mGravity = Gravity.START | Gravity.TOP; private final Rect mContentBounds = new Rect(); private final Rect mSelfBounds = new Rect(); private int mIgnoreGravity; private SortedSet<View> mTopToBottomLeftToRightSet = null; private boolean mDirtyHierarchy; private View[] mSortedHorizontalChildren; private View[] mSortedVerticalChildren; private final DependencyGraph mGraph = new DependencyGraph(); // Compatibility hack. Old versions of the platform had problems // with MeasureSpec value overflow and RelativeLayout was one source of them. // Some apps came to rely on them. :( private boolean mAllowBrokenMeasureSpecs = false; // Compatibility hack. Old versions of the platform would not take // margins and padding into account when generating the height measure spec // for children during the horizontal measure pass. private boolean mMeasureVerticalWithPaddingMargin = false; // A default width used for RTL measure pass
Value reduced so as not to interfere with View's measurement spec. flags. See: View.MEASURED_SIZE_MASK. View.MEASURED_STATE_TOO_SMALL.
/** * Value reduced so as not to interfere with View's measurement spec. flags. See: * {@link View#MEASURED_SIZE_MASK}. * {@link View#MEASURED_STATE_TOO_SMALL}. **/
private static final int DEFAULT_WIDTH = 0x00010000; public RelativeLayout(Context context) { this(context, null); } public RelativeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initFromAttributes(context, attrs, defStyleAttr, defStyleRes); queryCompatibilityModes(context); } private void initFromAttributes( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.RelativeLayout, defStyleAttr, defStyleRes); mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID); mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity); a.recycle(); } private void queryCompatibilityModes(Context context) { int version = context.getApplicationInfo().targetSdkVersion; mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1; mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2; } @Override public boolean shouldDelayChildPressedState() { return false; }
Defines which View is ignored when the gravity is applied. This setting has no effect if the gravity is Gravity.START | Gravity.TOP.
Params:
  • viewId – The id of the View to be ignored by gravity, or 0 if no View should be ignored.
See Also:
@attrref android.R.styleable#RelativeLayout_ignoreGravity
/** * Defines which View is ignored when the gravity is applied. This setting has no * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>. * * @param viewId The id of the View to be ignored by gravity, or 0 if no View * should be ignored. * * @see #setGravity(int) * * @attr ref android.R.styleable#RelativeLayout_ignoreGravity */
@android.view.RemotableViewMethod public void setIgnoreGravity(int viewId) { mIgnoreGravity = viewId; }
Describes how the child views are positioned.
See Also:
Returns:the gravity.
@attrref android.R.styleable#RelativeLayout_gravity
/** * Describes how the child views are positioned. * * @return the gravity. * * @see #setGravity(int) * @see android.view.Gravity * * @attr ref android.R.styleable#RelativeLayout_gravity */
public int getGravity() { return mGravity; }
Describes how the child views are positioned. Defaults to Gravity.START | Gravity.TOP.

Note that since RelativeLayout considers the positioning of each child relative to one another to be significant, setting gravity will affect the positioning of all children as a single unit within the parent. This happens after children have been relatively positioned.

Params:
See Also:
@attrref android.R.styleable#RelativeLayout_gravity
/** * Describes how the child views are positioned. Defaults to * <code>Gravity.START | Gravity.TOP</code>. * * <p>Note that since RelativeLayout considers the positioning of each child * relative to one another to be significant, setting gravity will affect * the positioning of all children as a single unit within the parent. * This happens after children have been relatively positioned.</p> * * @param gravity See {@link android.view.Gravity} * * @see #setHorizontalGravity(int) * @see #setVerticalGravity(int) * * @attr ref android.R.styleable#RelativeLayout_gravity */
@android.view.RemotableViewMethod public void setGravity(int gravity) { if (mGravity != gravity) { if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { gravity |= Gravity.START; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { gravity |= Gravity.TOP; } mGravity = gravity; requestLayout(); } } @android.view.RemotableViewMethod public void setHorizontalGravity(int horizontalGravity) { final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) { mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity; requestLayout(); } } @android.view.RemotableViewMethod public void setVerticalGravity(int verticalGravity) { final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK; if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) { mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity; requestLayout(); } } @Override public int getBaseline() { return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline(); } @Override public void requestLayout() { super.requestLayout(); mDirtyHierarchy = true; } private void sortChildren() { final int count = getChildCount(); if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) { mSortedVerticalChildren = new View[count]; } if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) { mSortedHorizontalChildren = new View[count]; } final DependencyGraph graph = mGraph; graph.clear(); for (int i = 0; i < count; i++) { graph.add(getChildAt(i)); } graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL); graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mDirtyHierarchy) { mDirtyHierarchy = false; sortChildren(); } int myWidth = -1; int myHeight = -1; int width = 0; int height = 0; final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); final int widthSize = MeasureSpec.getSize(widthMeasureSpec); final int heightSize = MeasureSpec.getSize(heightMeasureSpec); // Record our dimensions if they are known; if (widthMode != MeasureSpec.UNSPECIFIED) { myWidth = widthSize; } if (heightMode != MeasureSpec.UNSPECIFIED) { myHeight = heightSize; } if (widthMode == MeasureSpec.EXACTLY) { width = myWidth; } if (heightMode == MeasureSpec.EXACTLY) { height = myHeight; } View ignore = null; int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; final boolean horizontalGravity = gravity != Gravity.START && gravity != 0; gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0; int left = Integer.MAX_VALUE; int top = Integer.MAX_VALUE; int right = Integer.MIN_VALUE; int bottom = Integer.MIN_VALUE; boolean offsetHorizontalAxis = false; boolean offsetVerticalAxis = false; if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { ignore = findViewById(mIgnoreGravity); } final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY; final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY; // We need to know our size for doing the correct computation of children positioning in RTL // mode but there is no practical way to get it instead of running the code below. // So, instead of running the code twice, we just set the width to a "default display width" // before the computation and then, as a last pass, we will update their real position with // an offset equals to "DEFAULT_WIDTH - width". final int layoutDirection = getLayoutDirection(); if (isLayoutRtl() && myWidth == -1) { myWidth = DEFAULT_WIDTH; } View[] views = mSortedHorizontalChildren; int count = views.length; for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); int[] rules = params.getRules(layoutDirection); applyHorizontalSizeRules(params, myWidth, rules); measureChildHorizontal(child, params, myWidth, myHeight); if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) { offsetHorizontalAxis = true; } } } views = mSortedVerticalChildren; count = views.length; final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); applyVerticalSizeRules(params, myHeight, child.getBaseline()); measureChild(child, params, myWidth, myHeight); if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) { offsetVerticalAxis = true; } if (isWrapContentWidth) { if (isLayoutRtl()) { if (targetSdkVersion < Build.VERSION_CODES.KITKAT) { width = Math.max(width, myWidth - params.mLeft); } else { width = Math.max(width, myWidth - params.mLeft + params.leftMargin); } } else { if (targetSdkVersion < Build.VERSION_CODES.KITKAT) { width = Math.max(width, params.mRight); } else { width = Math.max(width, params.mRight + params.rightMargin); } } } if (isWrapContentHeight) { if (targetSdkVersion < Build.VERSION_CODES.KITKAT) { height = Math.max(height, params.mBottom); } else { height = Math.max(height, params.mBottom + params.bottomMargin); } } if (child != ignore || verticalGravity) { left = Math.min(left, params.mLeft - params.leftMargin); top = Math.min(top, params.mTop - params.topMargin); } if (child != ignore || horizontalGravity) { right = Math.max(right, params.mRight + params.rightMargin); bottom = Math.max(bottom, params.mBottom + params.bottomMargin); } } } // Use the top-start-most laid out view as the baseline. RTL offsets are // applied later, so we can use the left-most edge as the starting edge. View baselineView = null; LayoutParams baselineParams = null; for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams childParams = (LayoutParams) child.getLayoutParams(); if (baselineView == null || baselineParams == null || compareLayoutPosition(childParams, baselineParams) < 0) { baselineView = child; baselineParams = childParams; } } } mBaselineView = baselineView; if (isWrapContentWidth) { // Width already has left padding in it since it was calculated by looking at // the right of each child view width += mPaddingRight; if (mLayoutParams != null && mLayoutParams.width >= 0) { width = Math.max(width, mLayoutParams.width); } width = Math.max(width, getSuggestedMinimumWidth()); width = resolveSize(width, widthMeasureSpec); if (offsetHorizontalAxis) { for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); final int[] rules = params.getRules(layoutDirection); if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { centerHorizontal(child, params, width); } else if (rules[ALIGN_PARENT_RIGHT] != 0) { final int childWidth = child.getMeasuredWidth(); params.mLeft = width - mPaddingRight - childWidth; params.mRight = params.mLeft + childWidth; } } } } } if (isWrapContentHeight) { // Height already has top padding in it since it was calculated by looking at // the bottom of each child view height += mPaddingBottom; if (mLayoutParams != null && mLayoutParams.height >= 0) { height = Math.max(height, mLayoutParams.height); } height = Math.max(height, getSuggestedMinimumHeight()); height = resolveSize(height, heightMeasureSpec); if (offsetVerticalAxis) { for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); final int[] rules = params.getRules(layoutDirection); if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { centerVertical(child, params, height); } else if (rules[ALIGN_PARENT_BOTTOM] != 0) { final int childHeight = child.getMeasuredHeight(); params.mTop = height - mPaddingBottom - childHeight; params.mBottom = params.mTop + childHeight; } } } } } if (horizontalGravity || verticalGravity) { final Rect selfBounds = mSelfBounds; selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight, height - mPaddingBottom); final Rect contentBounds = mContentBounds; Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds, layoutDirection); final int horizontalOffset = contentBounds.left - left; final int verticalOffset = contentBounds.top - top; if (horizontalOffset != 0 || verticalOffset != 0) { for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE && child != ignore) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); if (horizontalGravity) { params.mLeft += horizontalOffset; params.mRight += horizontalOffset; } if (verticalGravity) { params.mTop += verticalOffset; params.mBottom += verticalOffset; } } } } } if (isLayoutRtl()) { final int offsetWidth = myWidth - width; for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); params.mLeft -= offsetWidth; params.mRight -= offsetWidth; } } } setMeasuredDimension(width, height); }
Returns:a negative number if the top of p1 is above the top of p2 or if they have identical top values and the left of p1 is to the left of p2, or a positive number otherwise
/** * @return a negative number if the top of {@code p1} is above the top of * {@code p2} or if they have identical top values and the left of * {@code p1} is to the left of {@code p2}, or a positive number * otherwise */
private int compareLayoutPosition(LayoutParams p1, LayoutParams p2) { final int topDiff = p1.mTop - p2.mTop; if (topDiff != 0) { return topDiff; } return p1.mLeft - p2.mLeft; }
Measure a child. The child should have left, top, right and bottom information stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means that the view can extend up to the corresponding edge.
Params:
  • child – Child to measure
  • params – LayoutParams associated with child
  • myWidth – Width of the the RelativeLayout
  • myHeight – Height of the RelativeLayout
/** * Measure a child. The child should have left, top, right and bottom information * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means * that the view can extend up to the corresponding edge. * * @param child Child to measure * @param params LayoutParams associated with child * @param myWidth Width of the the RelativeLayout * @param myHeight Height of the RelativeLayout */
private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) { int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight, params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight, myWidth); int childHeightMeasureSpec = getChildMeasureSpec(params.mTop, params.mBottom, params.height, params.topMargin, params.bottomMargin, mPaddingTop, mPaddingBottom, myHeight); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } private void measureChildHorizontal( View child, LayoutParams params, int myWidth, int myHeight) { final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight, params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight, myWidth); final int childHeightMeasureSpec; if (myHeight < 0 && !mAllowBrokenMeasureSpecs) { if (params.height >= 0) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( params.height, MeasureSpec.EXACTLY); } else { // Negative values in a mySize/myWidth/myWidth value in // RelativeLayout measurement is code for, "we got an // unspecified mode in the RelativeLayout's measure spec." // Carry it forward. childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } } else { final int maxHeight; if (mMeasureVerticalWithPaddingMargin) { maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom - params.topMargin - params.bottomMargin); } else { maxHeight = Math.max(0, myHeight); } final int heightMode; if (params.height == LayoutParams.MATCH_PARENT) { heightMode = MeasureSpec.EXACTLY; } else { heightMode = MeasureSpec.AT_MOST; } childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
Get a measure spec that accounts for all of the constraints on this view. This includes size constraints imposed by the RelativeLayout as well as the View's desired dimension.
Params:
  • childStart – The left or top field of the child's layout params
  • childEnd – The right or bottom field of the child's layout params
  • childSize – The child's desired size (the width or height field of the child's layout params)
  • startMargin – The left or top margin
  • endMargin – The right or bottom margin
  • startPadding – mPaddingLeft or mPaddingTop
  • endPadding – mPaddingRight or mPaddingBottom
  • mySize – The width or height of this view (the RelativeLayout)
Returns:MeasureSpec for the child
/** * Get a measure spec that accounts for all of the constraints on this view. * This includes size constraints imposed by the RelativeLayout as well as * the View's desired dimension. * * @param childStart The left or top field of the child's layout params * @param childEnd The right or bottom field of the child's layout params * @param childSize The child's desired size (the width or height field of * the child's layout params) * @param startMargin The left or top margin * @param endMargin The right or bottom margin * @param startPadding mPaddingLeft or mPaddingTop * @param endPadding mPaddingRight or mPaddingBottom * @param mySize The width or height of this view (the RelativeLayout) * @return MeasureSpec for the child */
private int getChildMeasureSpec(int childStart, int childEnd, int childSize, int startMargin, int endMargin, int startPadding, int endPadding, int mySize) { int childSpecMode = 0; int childSpecSize = 0; // Negative values in a mySize value in RelativeLayout // measurement is code for, "we got an unspecified mode in the // RelativeLayout's measure spec." final boolean isUnspecified = mySize < 0; if (isUnspecified && !mAllowBrokenMeasureSpecs) { if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) { // Constraints fixed both edges, so child has an exact size. childSpecSize = Math.max(0, childEnd - childStart); childSpecMode = MeasureSpec.EXACTLY; } else if (childSize >= 0) { // The child specified an exact size. childSpecSize = childSize; childSpecMode = MeasureSpec.EXACTLY; } else { // Allow the child to be whatever size it wants. childSpecSize = 0; childSpecMode = MeasureSpec.UNSPECIFIED; } return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); } // Figure out start and end bounds. int tempStart = childStart; int tempEnd = childEnd; // If the view did not express a layout constraint for an edge, use // view's margins and our padding if (tempStart == VALUE_NOT_SET) { tempStart = startPadding + startMargin; } if (tempEnd == VALUE_NOT_SET) { tempEnd = mySize - endPadding - endMargin; } // Figure out maximum size available to this view final int maxAvailable = tempEnd - tempStart; if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) { // Constraints fixed both edges, so child must be an exact size. childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY; childSpecSize = Math.max(0, maxAvailable); } else { if (childSize >= 0) { // Child wanted an exact size. Give as much as possible. childSpecMode = MeasureSpec.EXACTLY; if (maxAvailable >= 0) { // We have a maximum size in this dimension. childSpecSize = Math.min(maxAvailable, childSize); } else { // We can grow in this dimension. childSpecSize = childSize; } } else if (childSize == LayoutParams.MATCH_PARENT) { // Child wanted to be as big as possible. Give all available // space. childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY; childSpecSize = Math.max(0, maxAvailable); } else if (childSize == LayoutParams.WRAP_CONTENT) { // Child wants to wrap content. Use AT_MOST to communicate // available space if we know our max size. if (maxAvailable >= 0) { // We have a maximum size in this dimension. childSpecMode = MeasureSpec.AT_MOST; childSpecSize = maxAvailable; } else { // We can grow in this dimension. Child can be as big as it // wants. childSpecMode = MeasureSpec.UNSPECIFIED; childSpecSize = 0; } } } return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); } private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth, boolean wrapContent) { final int layoutDirection = getLayoutDirection(); int[] rules = params.getRules(layoutDirection); if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) { // Right is fixed, but left varies params.mLeft = params.mRight - child.getMeasuredWidth(); } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) { // Left is fixed, but right varies params.mRight = params.mLeft + child.getMeasuredWidth(); } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) { // Both left and right vary if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { if (!wrapContent) { centerHorizontal(child, params, myWidth); } else { positionAtEdge(child, params, myWidth); } return true; } else { // This is the default case. For RTL we start from the right and for LTR we start // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL. positionAtEdge(child, params, myWidth); } } return rules[ALIGN_PARENT_END] != 0; } private void positionAtEdge(View child, LayoutParams params, int myWidth) { if (isLayoutRtl()) { params.mRight = myWidth - mPaddingRight - params.rightMargin; params.mLeft = params.mRight - child.getMeasuredWidth(); } else { params.mLeft = mPaddingLeft + params.leftMargin; params.mRight = params.mLeft + child.getMeasuredWidth(); } } private boolean positionChildVertical(View child, LayoutParams params, int myHeight, boolean wrapContent) { int[] rules = params.getRules(); if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) { // Bottom is fixed, but top varies params.mTop = params.mBottom - child.getMeasuredHeight(); } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) { // Top is fixed, but bottom varies params.mBottom = params.mTop + child.getMeasuredHeight(); } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) { // Both top and bottom vary if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { if (!wrapContent) { centerVertical(child, params, myHeight); } else { params.mTop = mPaddingTop + params.topMargin; params.mBottom = params.mTop + child.getMeasuredHeight(); } return true; } else { params.mTop = mPaddingTop + params.topMargin; params.mBottom = params.mTop + child.getMeasuredHeight(); } } return rules[ALIGN_PARENT_BOTTOM] != 0; } private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) { RelativeLayout.LayoutParams anchorParams; // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example: // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it // wants to the right // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it // wants to the left // left=10, right=20 means the left and right ends are both fixed childParams.mLeft = VALUE_NOT_SET; childParams.mRight = VALUE_NOT_SET; anchorParams = getRelatedViewParams(rules, LEFT_OF); if (anchorParams != null) { childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin + childParams.rightMargin); } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) { if (myWidth >= 0) { childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; } } anchorParams = getRelatedViewParams(rules, RIGHT_OF); if (anchorParams != null) { childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin + childParams.leftMargin); } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) { childParams.mLeft = mPaddingLeft + childParams.leftMargin; } anchorParams = getRelatedViewParams(rules, ALIGN_LEFT); if (anchorParams != null) { childParams.mLeft = anchorParams.mLeft + childParams.leftMargin; } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) { childParams.mLeft = mPaddingLeft + childParams.leftMargin; } anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT); if (anchorParams != null) { childParams.mRight = anchorParams.mRight - childParams.rightMargin; } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) { if (myWidth >= 0) { childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; } } if (0 != rules[ALIGN_PARENT_LEFT]) { childParams.mLeft = mPaddingLeft + childParams.leftMargin; } if (0 != rules[ALIGN_PARENT_RIGHT]) { if (myWidth >= 0) { childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; } } } private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) { final int[] rules = childParams.getRules(); // Baseline alignment overrides any explicitly specified top or bottom. int baselineOffset = getRelatedViewBaselineOffset(rules); if (baselineOffset != -1) { if (myBaseline != -1) { baselineOffset -= myBaseline; } childParams.mTop = baselineOffset; childParams.mBottom = VALUE_NOT_SET; return; } RelativeLayout.LayoutParams anchorParams; childParams.mTop = VALUE_NOT_SET; childParams.mBottom = VALUE_NOT_SET; anchorParams = getRelatedViewParams(rules, ABOVE); if (anchorParams != null) { childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin + childParams.bottomMargin); } else if (childParams.alignWithParent && rules[ABOVE] != 0) { if (myHeight >= 0) { childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; } } anchorParams = getRelatedViewParams(rules, BELOW); if (anchorParams != null) { childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin + childParams.topMargin); } else if (childParams.alignWithParent && rules[BELOW] != 0) { childParams.mTop = mPaddingTop + childParams.topMargin; } anchorParams = getRelatedViewParams(rules, ALIGN_TOP); if (anchorParams != null) { childParams.mTop = anchorParams.mTop + childParams.topMargin; } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) { childParams.mTop = mPaddingTop + childParams.topMargin; } anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM); if (anchorParams != null) { childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin; } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) { if (myHeight >= 0) { childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; } } if (0 != rules[ALIGN_PARENT_TOP]) { childParams.mTop = mPaddingTop + childParams.topMargin; } if (0 != rules[ALIGN_PARENT_BOTTOM]) { if (myHeight >= 0) { childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; } } } private View getRelatedView(int[] rules, int relation) { int id = rules[relation]; if (id != 0) { DependencyGraph.Node node = mGraph.mKeyNodes.get(id); if (node == null) return null; View v = node.view; // Find the first non-GONE view up the chain while (v.getVisibility() == View.GONE) { rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection()); node = mGraph.mKeyNodes.get((rules[relation])); // ignore self dependency. for more info look in git commit: da3003 if (node == null || v == node.view) return null; v = node.view; } return v; } return null; } private LayoutParams getRelatedViewParams(int[] rules, int relation) { View v = getRelatedView(rules, relation); if (v != null) { ViewGroup.LayoutParams params = v.getLayoutParams(); if (params instanceof LayoutParams) { return (LayoutParams) v.getLayoutParams(); } } return null; } private int getRelatedViewBaselineOffset(int[] rules) { final View v = getRelatedView(rules, ALIGN_BASELINE); if (v != null) { final int baseline = v.getBaseline(); if (baseline != -1) { final ViewGroup.LayoutParams params = v.getLayoutParams(); if (params instanceof LayoutParams) { final LayoutParams anchorParams = (LayoutParams) v.getLayoutParams(); return anchorParams.mTop + baseline; } } } return -1; } private static void centerHorizontal(View child, LayoutParams params, int myWidth) { int childWidth = child.getMeasuredWidth(); int left = (myWidth - childWidth) / 2; params.mLeft = left; params.mRight = left + childWidth; } private static void centerVertical(View child, LayoutParams params, int myHeight) { int childHeight = child.getMeasuredHeight(); int top = (myHeight - childHeight) / 2; params.mTop = top; params.mBottom = top + childHeight; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // The layout has actually already been performed and the positions // cached. Apply the cached values to the children. final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { RelativeLayout.LayoutParams st = (RelativeLayout.LayoutParams) child.getLayoutParams(); child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom); } } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new RelativeLayout.LayoutParams(getContext(), attrs); }
Returns a set of layout parameters with a width of LayoutParams.WRAP_CONTENT, a height of LayoutParams.WRAP_CONTENT and no spanning.
/** * Returns a set of layout parameters with a width of * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}, * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning. */
@Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } // Override to allow type-checking of LayoutParams. @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof RelativeLayout.LayoutParams; } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { if (sPreserveMarginParamsInLayoutParamConversion) { if (lp instanceof LayoutParams) { return new LayoutParams((LayoutParams) lp); } else if (lp instanceof MarginLayoutParams) { return new LayoutParams((MarginLayoutParams) lp); } } return new LayoutParams(lp); }
@hide
/** @hide */
@Override public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { if (mTopToBottomLeftToRightSet == null) { mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator()); } // sort children top-to-bottom and left-to-right for (int i = 0, count = getChildCount(); i < count; i++) { mTopToBottomLeftToRightSet.add(getChildAt(i)); } for (View view : mTopToBottomLeftToRightSet) { if (view.getVisibility() == View.VISIBLE && view.dispatchPopulateAccessibilityEvent(event)) { mTopToBottomLeftToRightSet.clear(); return true; } } mTopToBottomLeftToRightSet.clear(); return false; } @Override public CharSequence getAccessibilityClassName() { return RelativeLayout.class.getName(); }
Compares two views in left-to-right and top-to-bottom fashion.
/** * Compares two views in left-to-right and top-to-bottom fashion. */
private class TopToBottomLeftToRightComparator implements Comparator<View> { public int compare(View first, View second) { // top - bottom int topDifference = first.getTop() - second.getTop(); if (topDifference != 0) { return topDifference; } // left - right int leftDifference = first.getLeft() - second.getLeft(); if (leftDifference != 0) { return leftDifference; } // break tie by height int heightDiference = first.getHeight() - second.getHeight(); if (heightDiference != 0) { return heightDiference; } // break tie by width int widthDiference = first.getWidth() - second.getWidth(); if (widthDiference != 0) { return widthDiference; } return 0; } }
Specifies how a view is positioned within a RelativeLayout. The relative layout containing the view uses the value of these layout parameters to determine where to position the view on the screen. If the view is not contained within a relative layout, these attributes are ignored. See the Relative Layout guide for example code demonstrating how to use relative layout’s layout parameters in a layout XML. To learn more about layout parameters and how they differ from typical view attributes, see the Layouts guide.
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
@attrref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
@attrref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
@attrref android.R.styleable#RelativeLayout_Layout_layout_above
@attrref android.R.styleable#RelativeLayout_Layout_layout_below
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignTop
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignRight
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
@attrref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
@attrref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
@attrref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
@attrref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
@attrref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignStart
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
@attrref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
/** * Specifies how a view is positioned within a {@link RelativeLayout}. * The relative layout containing the view uses the value of these layout parameters to * determine where to position the view on the screen. If the view is not contained * within a relative layout, these attributes are ignored. * * See the <a href="/guide/topics/ui/layout/relative.html"> * Relative Layout</a> guide for example code demonstrating how to use relative layout’s * layout parameters in a layout XML. * * To learn more about layout parameters and how they differ from typical view attributes, * see the <a href="/guide/topics/ui/declaring-layout.html#attributes"> * Layouts guide</a>. * * * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd */
public static class LayoutParams extends ViewGroup.MarginLayoutParams { @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = { @ViewDebug.IntToString(from = ABOVE, to = "above"), @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"), @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"), @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"), @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"), @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"), @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"), @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"), @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"), @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"), @ViewDebug.IntToString(from = BELOW, to = "below"), @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"), @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"), @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"), @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"), @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"), @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"), @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"), @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"), @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"), @ViewDebug.IntToString(from = START_OF, to = "startOf"), @ViewDebug.IntToString(from = END_OF, to = "endOf") }, mapping = { @ViewDebug.IntToString(from = TRUE, to = "true"), @ViewDebug.IntToString(from = 0, to = "false/NO_ID") }) private int[] mRules = new int[VERB_COUNT]; private int[] mInitialRules = new int[VERB_COUNT]; private int mLeft, mTop, mRight, mBottom;
Whether this view had any relative rules modified following the most recent resolution of layout direction.
/** * Whether this view had any relative rules modified following the most * recent resolution of layout direction. */
private boolean mNeedsLayoutResolution; private boolean mRulesChanged = false; private boolean mIsRtlCompatibilityMode = false;
When true, uses the parent as the anchor if the anchor doesn't exist or if the anchor's visibility is GONE.
/** * When true, uses the parent as the anchor if the anchor doesn't exist or if * the anchor's visibility is GONE. */
@ViewDebug.ExportedProperty(category = "layout") public boolean alignWithParent; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.RelativeLayout_Layout); final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 || !c.getApplicationInfo().hasRtlSupport()); final int[] rules = mRules; //noinspection MismatchedReadAndWriteOfArray final int[] initialRules = mInitialRules; final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); switch (attr) { case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing: alignWithParent = a.getBoolean(attr, false); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf: rules[LEFT_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf: rules[RIGHT_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above: rules[ABOVE] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below: rules[BELOW] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline: rules[ALIGN_BASELINE] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft: rules[ALIGN_LEFT] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop: rules[ALIGN_TOP] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight: rules[ALIGN_RIGHT] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom: rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft: rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop: rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight: rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom: rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent: rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal: rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical: rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf: rules[START_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf: rules[END_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart: rules[ALIGN_START] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd: rules[ALIGN_END] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart: rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd: rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0; break; } } mRulesChanged = true; System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT); a.recycle(); } public LayoutParams(int w, int h) { super(w, h); }
{@inheritDoc}
/** * {@inheritDoc} */
public LayoutParams(ViewGroup.LayoutParams source) { super(source); }
{@inheritDoc}
/** * {@inheritDoc} */
public LayoutParams(ViewGroup.MarginLayoutParams source) { super(source); }
Copy constructor. Clones the width, height, margin values, and rules of the source.
Params:
  • source – The layout params to copy from.
/** * Copy constructor. Clones the width, height, margin values, and rules * of the source. * * @param source The layout params to copy from. */
public LayoutParams(LayoutParams source) { super(source); this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode; this.mRulesChanged = source.mRulesChanged; this.alignWithParent = source.alignWithParent; System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT); System.arraycopy( source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT); } @Override public String debug(String output) { return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) + ", height=" + sizeToString(height) + " }"; }
Adds a layout rule to be interpreted by the RelativeLayout.

This method should only be used for verbs that don't refer to a sibling (ex. RelativeLayout.ALIGN_RIGHT) or take a boolean value (RelativeLayout.TRUE for true or 0 for false). To specify a verb that takes a subject, use addRule(int, int).

If the rule is relative to the layout direction (ex. RelativeLayout.ALIGN_PARENT_START), then the layout direction must be resolved using resolveLayoutDirection(int) before calling getRule(int) an absolute rule (ex. RelativeLayout.ALIGN_PARENT_LEFT.

Params:
See Also:
/** * Adds a layout rule to be interpreted by the RelativeLayout. * <p> * This method should only be used for verbs that don't refer to a * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean * value ({@link #TRUE} for true or 0 for false). To * specify a verb that takes a subject, use {@link #addRule(int, int)}. * <p> * If the rule is relative to the layout direction (ex. * {@link #ALIGN_PARENT_START}), then the layout direction must be * resolved using {@link #resolveLayoutDirection(int)} before calling * {@link #getRule(int)} an absolute rule (ex. * {@link #ALIGN_PARENT_LEFT}. * * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT} * @see #addRule(int, int) * @see #removeRule(int) * @see #getRule(int) */
public void addRule(int verb) { addRule(verb, TRUE); }
Adds a layout rule to be interpreted by the RelativeLayout.

Use this for verbs that refer to a sibling (ex. RelativeLayout.ALIGN_RIGHT) or take a boolean value (ex. RelativeLayout.CENTER_IN_PARENT).

If the rule is relative to the layout direction (ex. RelativeLayout.START_OF), then the layout direction must be resolved using resolveLayoutDirection(int) before calling getRule(int) with an absolute rule (ex. RelativeLayout.LEFT_OF.

Params:
See Also:
/** * Adds a layout rule to be interpreted by the RelativeLayout. * <p> * Use this for verbs that refer to a sibling (ex. * {@link #ALIGN_RIGHT}) or take a boolean value (ex. * {@link #CENTER_IN_PARENT}). * <p> * If the rule is relative to the layout direction (ex. * {@link #START_OF}), then the layout direction must be resolved using * {@link #resolveLayoutDirection(int)} before calling * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}. * * @param verb a layout verb, such as {@link #ALIGN_RIGHT} * @param subject the ID of another view to use as an anchor, or a * boolean value (represented as {@link #TRUE} for true * or 0 for false) * @see #addRule(int) * @see #removeRule(int) * @see #getRule(int) */
public void addRule(int verb, int subject) { // If we're removing a relative rule, we'll need to force layout // resolution the next time it's requested. if (!mNeedsLayoutResolution && isRelativeRule(verb) && mInitialRules[verb] != 0 && subject == 0) { mNeedsLayoutResolution = true; } mRules[verb] = subject; mInitialRules[verb] = subject; mRulesChanged = true; }
Removes a layout rule to be interpreted by the RelativeLayout.

If the rule is relative to the layout direction (ex. RelativeLayout.START_OF, RelativeLayout.ALIGN_PARENT_START, etc.) then the layout direction must be resolved using resolveLayoutDirection(int) before before calling getRule(int) with an absolute rule (ex. RelativeLayout.LEFT_OF.

Params:
  • verb – One of the verbs defined by RelativeLayout, such as ALIGN_WITH_PARENT_LEFT.
See Also:
/** * Removes a layout rule to be interpreted by the RelativeLayout. * <p> * If the rule is relative to the layout direction (ex. * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the * layout direction must be resolved using * {@link #resolveLayoutDirection(int)} before before calling * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}. * * @param verb One of the verbs defined by * {@link android.widget.RelativeLayout RelativeLayout}, such as * ALIGN_WITH_PARENT_LEFT. * @see #addRule(int) * @see #addRule(int, int) * @see #getRule(int) */
public void removeRule(int verb) { addRule(verb, 0); }
Returns the layout rule associated with a specific verb.
Params:
  • verb – one of the verbs defined by RelativeLayout, such as ALIGN_WITH_PARENT_LEFT
See Also:
Returns:the id of another view to use as an anchor, a boolean value (represented as RelativeLayout.TRUE for true or 0 for false), or -1 for verbs that don't refer to another sibling (for example, ALIGN_WITH_PARENT_BOTTOM)
/** * Returns the layout rule associated with a specific verb. * * @param verb one of the verbs defined by {@link RelativeLayout}, such * as ALIGN_WITH_PARENT_LEFT * @return the id of another view to use as an anchor, a boolean value * (represented as {@link RelativeLayout#TRUE} for true * or 0 for false), or -1 for verbs that don't refer to another * sibling (for example, ALIGN_WITH_PARENT_BOTTOM) * @see #addRule(int) * @see #addRule(int, int) */
public int getRule(int verb) { return mRules[verb]; } private boolean hasRelativeRules() { return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 || mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 || mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0); } private boolean isRelativeRule(int rule) { return rule == START_OF || rule == END_OF || rule == ALIGN_START || rule == ALIGN_END || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END; } // The way we are resolving rules depends on the layout direction and if we are pre JB MR1 // or not. // // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having // predominance over any "start/end" rules that could have been defined. A special case: // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we // resolve those "start"/"end" rules to "left"/"right" respectively. // // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right" // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules. // // In all cases, the result of the resolution should clear the "start"/"end" rules to leave // only the "left"/"right" rules at the end. private void resolveRules(int layoutDirection) { final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL); // Reset to initial state System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT); // Apply rules depending on direction and if we are in RTL compatibility mode if (mIsRtlCompatibilityMode) { if (mRules[ALIGN_START] != 0) { if (mRules[ALIGN_LEFT] == 0) { // "left" rule is not defined but "start" rule is: use the "start" rule as // the "left" rule mRules[ALIGN_LEFT] = mRules[ALIGN_START]; } mRules[ALIGN_START] = 0; } if (mRules[ALIGN_END] != 0) { if (mRules[ALIGN_RIGHT] == 0) { // "right" rule is not defined but "end" rule is: use the "end" rule as the // "right" rule mRules[ALIGN_RIGHT] = mRules[ALIGN_END]; } mRules[ALIGN_END] = 0; } if (mRules[START_OF] != 0) { if (mRules[LEFT_OF] == 0) { // "left" rule is not defined but "start" rule is: use the "start" rule as // the "left" rule mRules[LEFT_OF] = mRules[START_OF]; } mRules[START_OF] = 0; } if (mRules[END_OF] != 0) { if (mRules[RIGHT_OF] == 0) { // "right" rule is not defined but "end" rule is: use the "end" rule as the // "right" rule mRules[RIGHT_OF] = mRules[END_OF]; } mRules[END_OF] = 0; } if (mRules[ALIGN_PARENT_START] != 0) { if (mRules[ALIGN_PARENT_LEFT] == 0) { // "left" rule is not defined but "start" rule is: use the "start" rule as // the "left" rule mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START]; } mRules[ALIGN_PARENT_START] = 0; } if (mRules[ALIGN_PARENT_END] != 0) { if (mRules[ALIGN_PARENT_RIGHT] == 0) { // "right" rule is not defined but "end" rule is: use the "end" rule as the // "right" rule mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END]; } mRules[ALIGN_PARENT_END] = 0; } } else { // JB MR1+ case if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) && (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) { // "start"/"end" rules take precedence over "left"/"right" rules mRules[ALIGN_LEFT] = 0; mRules[ALIGN_RIGHT] = 0; } if (mRules[ALIGN_START] != 0) { // "start" rule resolved to "left" or "right" depending on the direction mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START]; mRules[ALIGN_START] = 0; } if (mRules[ALIGN_END] != 0) { // "end" rule resolved to "left" or "right" depending on the direction mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END]; mRules[ALIGN_END] = 0; } if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) && (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) { // "start"/"end" rules take precedence over "left"/"right" rules mRules[LEFT_OF] = 0; mRules[RIGHT_OF] = 0; } if (mRules[START_OF] != 0) { // "start" rule resolved to "left" or "right" depending on the direction mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF]; mRules[START_OF] = 0; } if (mRules[END_OF] != 0) { // "end" rule resolved to "left" or "right" depending on the direction mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF]; mRules[END_OF] = 0; } if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) && (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) { // "start"/"end" rules take precedence over "left"/"right" rules mRules[ALIGN_PARENT_LEFT] = 0; mRules[ALIGN_PARENT_RIGHT] = 0; } if (mRules[ALIGN_PARENT_START] != 0) { // "start" rule resolved to "left" or "right" depending on the direction mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START]; mRules[ALIGN_PARENT_START] = 0; } if (mRules[ALIGN_PARENT_END] != 0) { // "end" rule resolved to "left" or "right" depending on the direction mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END]; mRules[ALIGN_PARENT_END] = 0; } } mRulesChanged = false; mNeedsLayoutResolution = false; }
Retrieves a complete list of all supported rules, where the index is the rule verb, and the element value is the value specified, or "false" if it was never set. If there are relative rules defined (*_START / *_END), they will be resolved depending on the layout direction.
Params:
See Also:
Returns:the supported rules
@hide
/** * Retrieves a complete list of all supported rules, where the index is the rule * verb, and the element value is the value specified, or "false" if it was never * set. If there are relative rules defined (*_START / *_END), they will be resolved * depending on the layout direction. * * @param layoutDirection the direction of the layout. * Should be either {@link View#LAYOUT_DIRECTION_LTR} * or {@link View#LAYOUT_DIRECTION_RTL} * @return the supported rules * @see #addRule(int, int) * * @hide */
public int[] getRules(int layoutDirection) { resolveLayoutDirection(layoutDirection); return mRules; }
Retrieves a complete list of all supported rules, where the index is the rule verb, and the element value is the value specified, or "false" if it was never set. There will be no resolution of relative rules done.
See Also:
Returns:the supported rules
/** * Retrieves a complete list of all supported rules, where the index is the rule * verb, and the element value is the value specified, or "false" if it was never * set. There will be no resolution of relative rules done. * * @return the supported rules * @see #addRule(int, int) */
public int[] getRules() { return mRules; }
This will be called by View.requestLayout() to resolve layout parameters that are relative to the layout direction.

After this method is called, any rules using layout-relative verbs (ex. RelativeLayout.START_OF) previously added via addRule(int) may only be accessed via their resolved absolute verbs (ex. RelativeLayout.LEFT_OF).

/** * This will be called by {@link android.view.View#requestLayout()} to * resolve layout parameters that are relative to the layout direction. * <p> * After this method is called, any rules using layout-relative verbs * (ex. {@link #START_OF}) previously added via {@link #addRule(int)} * may only be accessed via their resolved absolute verbs (ex. * {@link #LEFT_OF}). */
@Override public void resolveLayoutDirection(int layoutDirection) { if (shouldResolveLayoutDirection(layoutDirection)) { resolveRules(layoutDirection); } // This will set the layout direction. super.resolveLayoutDirection(layoutDirection); } private boolean shouldResolveLayoutDirection(int layoutDirection) { return (mNeedsLayoutResolution || hasRelativeRules()) && (mRulesChanged || layoutDirection != getLayoutDirection()); }
@hide
/** @hide */
@Override protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { super.encodeProperties(encoder); encoder.addProperty("layout:alignWithParent", alignWithParent); } } private static class DependencyGraph {
List of all views in the graph.
/** * List of all views in the graph. */
private ArrayList<Node> mNodes = new ArrayList<Node>();
List of nodes in the graph. Each node is identified by its view id (see View#getId()).
/** * List of nodes in the graph. Each node is identified by its * view id (see View#getId()). */
private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
Temporary data structure used to build the list of roots for this graph.
/** * Temporary data structure used to build the list of roots * for this graph. */
private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
Clears the graph.
/** * Clears the graph. */
void clear() { final ArrayList<Node> nodes = mNodes; final int count = nodes.size(); for (int i = 0; i < count; i++) { nodes.get(i).release(); } nodes.clear(); mKeyNodes.clear(); mRoots.clear(); }
Adds a view to the graph.
Params:
  • view – The view to be added as a node to the graph.
/** * Adds a view to the graph. * * @param view The view to be added as a node to the graph. */
void add(View view) { final int id = view.getId(); final Node node = Node.acquire(view); if (id != View.NO_ID) { mKeyNodes.put(id, node); } mNodes.add(node); }
Builds a sorted list of views. The sorting order depends on the dependencies between the view. For instance, if view C needs view A to be processed first and view A needs view B to be processed first, the dependency graph is: B -> A -> C. The sorted array will contain views B, A and C in this order.
Params:
  • sorted – The sorted list of views. The length of this array must be equal to getChildCount().
  • rules – The list of rules to take into account.
/** * Builds a sorted list of views. The sorting order depends on the dependencies * between the view. For instance, if view C needs view A to be processed first * and view A needs view B to be processed first, the dependency graph * is: B -> A -> C. The sorted array will contain views B, A and C in this order. * * @param sorted The sorted list of views. The length of this array must * be equal to getChildCount(). * @param rules The list of rules to take into account. */
void getSortedViews(View[] sorted, int... rules) { final ArrayDeque<Node> roots = findRoots(rules); int index = 0; Node node; while ((node = roots.pollLast()) != null) { final View view = node.view; final int key = view.getId(); sorted[index++] = view; final ArrayMap<Node, DependencyGraph> dependents = node.dependents; final int count = dependents.size(); for (int i = 0; i < count; i++) { final Node dependent = dependents.keyAt(i); final SparseArray<Node> dependencies = dependent.dependencies; dependencies.remove(key); if (dependencies.size() == 0) { roots.add(dependent); } } } if (index < sorted.length) { throw new IllegalStateException("Circular dependencies cannot exist" + " in RelativeLayout"); } }
Finds the roots of the graph. A root is a node with no dependency and with [0..n] dependents.
Params:
  • rulesFilter – The list of rules to consider when building the dependencies
Returns:A list of node, each being a root of the graph
/** * Finds the roots of the graph. A root is a node with no dependency and * with [0..n] dependents. * * @param rulesFilter The list of rules to consider when building the * dependencies * * @return A list of node, each being a root of the graph */
private ArrayDeque<Node> findRoots(int[] rulesFilter) { final SparseArray<Node> keyNodes = mKeyNodes; final ArrayList<Node> nodes = mNodes; final int count = nodes.size(); // Find roots can be invoked several times, so make sure to clear // all dependents and dependencies before running the algorithm for (int i = 0; i < count; i++) { final Node node = nodes.get(i); node.dependents.clear(); node.dependencies.clear(); } // Builds up the dependents and dependencies for each node of the graph for (int i = 0; i < count; i++) { final Node node = nodes.get(i); final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams(); final int[] rules = layoutParams.mRules; final int rulesCount = rulesFilter.length; // Look only the the rules passed in parameter, this way we build only the // dependencies for a specific set of rules for (int j = 0; j < rulesCount; j++) { final int rule = rules[rulesFilter[j]]; if (rule > 0) { // The node this node depends on final Node dependency = keyNodes.get(rule); // Skip unknowns and self dependencies if (dependency == null || dependency == node) { continue; } // Add the current node as a dependent dependency.dependents.put(node, this); // Add a dependency to the current node node.dependencies.put(rule, dependency); } } } final ArrayDeque<Node> roots = mRoots; roots.clear(); // Finds all the roots in the graph: all nodes with no dependencies for (int i = 0; i < count; i++) { final Node node = nodes.get(i); if (node.dependencies.size() == 0) roots.addLast(node); } return roots; }
A node in the dependency graph. A node is a view, its list of dependencies and its list of dependents. A node with no dependent is considered a root of the graph.
/** * A node in the dependency graph. A node is a view, its list of dependencies * and its list of dependents. * * A node with no dependent is considered a root of the graph. */
static class Node {
The view representing this node in the layout.
/** * The view representing this node in the layout. */
View view;
The list of dependents for this node; a dependent is a node that needs this node to be processed first.
/** * The list of dependents for this node; a dependent is a node * that needs this node to be processed first. */
final ArrayMap<Node, DependencyGraph> dependents = new ArrayMap<Node, DependencyGraph>();
The list of dependencies for this node.
/** * The list of dependencies for this node. */
final SparseArray<Node> dependencies = new SparseArray<Node>(); /* * START POOL IMPLEMENTATION */ // The pool is static, so all nodes instances are shared across // activities, that's why we give it a rather high limit private static final int POOL_LIMIT = 100; private static final SynchronizedPool<Node> sPool = new SynchronizedPool<Node>(POOL_LIMIT); static Node acquire(View view) { Node node = sPool.acquire(); if (node == null) { node = new Node(); } node.view = view; return node; } void release() { view = null; dependents.clear(); dependencies.clear(); sPool.release(this); } /* * END POOL IMPLEMENTATION */ } } }