/* * 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 android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.Gravity; import android.view.ViewDebug; import android.widget.RemoteViews.RemoteView; import android.graphics.Rect; import com.android.internal.R; /** * A Layout where the positions of the children can be described in relation to each other or to the * parent. For the sake of efficiency, the relations between views are evaluated in one pass, so if * view Y is dependent on the position of view X, make sure the view X comes first in the layout. * * <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> * 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. */ public static final int LEFT_OF = 0; /** * 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. */ public static final int ABOVE = 2; /** * 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. */ public static final int ALIGN_BASELINE = 4; /** * 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. */ public static final int ALIGN_TOP = 6; /** * 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. */ public static final int ALIGN_BOTTOM = 8; /** * 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. */ public static final int ALIGN_PARENT_TOP = 10; /** * 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. */ public static final int ALIGN_PARENT_BOTTOM = 12; /** * 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. */ public static final int CENTER_HORIZONTAL = 14; /** * Rule that centers the child vertically with respect to the * bounds of its RelativeLayout parent. */ public static final int CENTER_VERTICAL = 15; private static final int VERB_COUNT = 16; private View mBaselineView = null; private boolean mHasBaselineAlignedChild; private int mGravity = Gravity.LEFT | Gravity.TOP; private final Rect mContentBounds = new Rect(); private final Rect mSelfBounds = new Rect(); private int mIgnoreGravity; public RelativeLayout(Context context) { super(context); } public RelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); initFromAttributes(context, attrs); } public RelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initFromAttributes(context, attrs); } private void initFromAttributes(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout); mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID); mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity); a.recycle(); } /** * Defines which View is ignored when the gravity is applied. This setting has no * effect if the gravity is <code>Gravity.LEFT | 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. Defaults to * <code>Gravity.LEFT | Gravity.TOP</code>. * * @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.HORIZONTAL_GRAVITY_MASK) == 0) { gravity |= Gravity.LEFT; } 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.HORIZONTAL_GRAVITY_MASK; if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) { mGravity = (mGravity & ~Gravity.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 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int myWidth = -1; int myHeight = -1; int width = 0; int height = 0; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); 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; } int len = this.getChildCount(); mHasBaselineAlignedChild = false; View ignore = null; int gravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK; final boolean horizontalGravity = gravity != Gravity.LEFT && 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; if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { ignore = findViewById(mIgnoreGravity); } for (int i = 0; i < len; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); applySizeRules(params, myWidth, myHeight); measureChild(child, params, myWidth, myHeight); positionChild(child, params, myWidth, myHeight); if (widthMode != MeasureSpec.EXACTLY) { width = Math.max(width, params.mRight); } if (heightMode != MeasureSpec.EXACTLY) { height = Math.max(height, params.mBottom); } 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); } } } if (mHasBaselineAlignedChild) { for (int i = 0; i < len; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); alignBaseline(child, params); 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); } } } } if (widthMode != MeasureSpec.EXACTLY) { // Width already has left padding in it since it was calculated by looking at // the right of each child view width += mPaddingRight; if (mLayoutParams.width >= 0) { width = Math.max(width, mLayoutParams.width); } width = Math.max(width, getSuggestedMinimumWidth()); width = resolveSize(width, widthMeasureSpec); } if (heightMode != MeasureSpec.EXACTLY) { // Height already has top padding in it since it was calculated by looking at // the bottom of each child view height += mPaddingBottom; if (mLayoutParams.height >= 0) { height = Math.max(height, mLayoutParams.height); } height = Math.max(height, getSuggestedMinimumHeight()); height = resolveSize(height, heightMeasureSpec); } 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); final int horizontalOffset = contentBounds.left - left; final int verticalOffset = contentBounds.top - top; if (horizontalOffset != 0 || verticalOffset != 0) { for (int i = 0; i < len; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE && child != ignore) { LayoutParams params = (LayoutParams) child.getLayoutParams(); params.mLeft += horizontalOffset; params.mRight += horizontalOffset; params.mTop += verticalOffset; params.mBottom += verticalOffset; } } } } setMeasuredDimension(width, height); } private void alignBaseline(View child, LayoutParams params) { int[] rules = params.getRules(); int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE); if (anchorBaseline != -1) { LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE); if (anchorParams != null) { int offset = anchorParams.mTop + anchorBaseline; int baseline = child.getBaseline(); if (baseline != -1) { offset -= baseline; } int height = params.mBottom - params.mTop; params.mTop = offset; params.mBottom = params.mTop + height; } } if (mBaselineView == null) { mBaselineView = child; } else { LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams(); if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) { mBaselineView = child; } } } /** * Measure a child. The child should have left, top, right and bottom information * stored in its LayoutParams. If any of these values is -1 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); } /** * Get a measure spec that accounts for all of the constraints on this view. * This includes size contstraints 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; // 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 < 0) { tempStart = startPadding + startMargin; } if (tempEnd < 0) { tempEnd = mySize - endPadding - endMargin; } // Figure out maximum size available to this view int maxAvailable = tempEnd - tempStart; if (childStart >= 0 && childEnd >= 0) { // Constraints fixed both edges, so child must be an exact size childSpecMode = MeasureSpec.EXACTLY; childSpecSize = maxAvailable; } else { if (childSize >= 0) { // Child wanted an exact size. Give as much as possible childSpecMode = MeasureSpec.EXACTLY; if (maxAvailable >= 0) { // We have a maxmum size in this dimension. childSpecSize = Math.min(maxAvailable, childSize); } else { // We can grow in this dimension. childSpecSize = childSize; } } else if (childSize == LayoutParams.FILL_PARENT) { // Child wanted to be as big as possible. Give all availble // space childSpecMode = MeasureSpec.EXACTLY; childSpecSize = 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 maxmum 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); } /** * After the child has been measured, assign it a position. Some views may * already have final values for l,t,r,b. Others may have one or both edges * unfixed (i.e. set to -1) in each dimension. These will get positioned * based on which edge is fixed, the view's desired dimension, and whether * or not it is centered. * * @param child Child to position * @param params LayoutParams associated with child * @param myWidth Width of the the RelativeLayout * @param myHeight Height of the RelativeLayout */ private void positionChild(View child, LayoutParams params, int myWidth, int myHeight) { int[] rules = params.getRules(); if (params.mLeft < 0 && params.mRight >= 0) { // Right is fixed, but left varies params.mLeft = params.mRight - child.getMeasuredWidth(); } else if (params.mLeft >= 0 && params.mRight < 0) { // Left is fixed, but right varies params.mRight = params.mLeft + child.getMeasuredWidth(); } else if (params.mLeft < 0 && params.mRight < 0) { // Both left and right vary if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) { centerHorizontal(child, params, myWidth); } else { params.mLeft = mPaddingLeft + params.leftMargin; params.mRight = params.mLeft + child.getMeasuredWidth(); } } if (params.mTop < 0 && params.mBottom >= 0) { // Bottom is fixed, but top varies params.mTop = params.mBottom - child.getMeasuredHeight(); } else if (params.mTop >= 0 && params.mBottom < 0) { // Top is fixed, but bottom varies params.mBottom = params.mTop + child.getMeasuredHeight(); } else if (params.mTop < 0 && params.mBottom < 0) { // Both top and bottom vary if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) { centerVertical(child, params, myHeight); } else { params.mTop = mPaddingTop + params.topMargin; params.mBottom = params.mTop + child.getMeasuredHeight(); } } } /** * Set l,t,r,b values in the LayoutParams for one view based on its layout rules. * Big assumption #1: All antecedents of this view have been sized & positioned * Big assumption #2: The dimensions of the parent view (the RelativeLayout) * are already known if they are needed. * * @param childParams LayoutParams for the view being positioned * @param myWidth Width of the the RelativeLayout * @param myHeight Height of the RelativeLayout */ private void applySizeRules(LayoutParams childParams, int myWidth, int myHeight) { int[] rules = childParams.getRules(); RelativeLayout.LayoutParams anchorParams; // -1 indicated a "soft requirement" in that direction. For example: // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right // left =-1, 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 = -1; childParams.mRight = -1; 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; } else { // FIXME uh oh... } } 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; } else { // FIXME uh oh... } } 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; } else { // FIXME uh oh... } } childParams.mTop = -1; childParams.mBottom = -1; 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; } else { // FIXME uh oh... } } 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; } else { // FIXME uh oh... } } 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; } else { // FIXME uh oh... } } if (rules[ALIGN_BASELINE] != 0) { mHasBaselineAlignedChild = true; } } private View getRelatedView(int[] rules, int relation) { int id = rules[relation]; if (id != 0) { View v = findViewById(id); if (v == null) { return null; } // Find the first non-GONE view up the chain while (v.getVisibility() == View.GONE) { rules = ((LayoutParams) v.getLayoutParams()).getRules(); v = v.findViewById(rules[relation]); if (v == null) { return null; } } 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 getRelatedViewBaseline(int[] rules, int relation) { View v = getRelatedView(rules, relation); if (v != null) { return v.getBaseline(); } return -1; } private 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 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. 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 * {@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 p) { return new LayoutParams(p); } /** * Per-child layout information associated with RelativeLayout. * * @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 */ public static class LayoutParams extends ViewGroup.MarginLayoutParams { @ViewDebug.ExportedProperty(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") }, mapping = { @ViewDebug.IntToString(from = TRUE, to = "true"), @ViewDebug.IntToString(from = 0, to = "NO_ID") }) private int[] mRules = new int[VERB_COUNT]; private int mLeft, mTop, mRight, mBottom; /** * When true, uses the parent as the anchor if the anchor doesn't exist or if * the anchor's visibility is GONE. */ @ViewDebug.ExportedProperty 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[] rules = mRules; 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; } } a.recycle(); } public LayoutParams(int w, int h) { super(w, h); } /** * {@inheritDoc} */ public LayoutParams(ViewGroup.LayoutParams source) { super(source); } /** * {@inheritDoc} */ public LayoutParams(ViewGroup.MarginLayoutParams source) { super(source); } @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 constraints that don't refer to another sibling * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE} * for true or - for false). To specify a verb that takes a subject, use * {@link #addRule(int, int)} instead. * * @param verb One of the verbs defined by * {@link android.widget.RelativeLayout RelativeLayout}, such as * ALIGN_WITH_PARENT_LEFT. * @see #addRule(int, int) */ public void addRule(int verb) { mRules[verb] = TRUE; } /** * Adds a layout rule to be interpreted by the RelativeLayout. Use this for * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean * value (VISIBLE). * * @param verb One of the verbs defined by * {@link android.widget.RelativeLayout RelativeLayout}, such as * ALIGN_WITH_PARENT_LEFT. * @param anchor The id of another view to use as an anchor, * or a boolean value(represented as {@link RelativeLayout#TRUE}) * for true or 0 for false). For verbs that don't refer to another sibling * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1. * @see #addRule(int) */ public void addRule(int verb, int anchor) { mRules[verb] = anchor; } /** * 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. * * @return the supported rules * @see #addRule(int, int) */ public int[] getRules() { return mRules; } } }