summaryrefslogtreecommitdiffstats
path: root/core/java/android/widget/RelativeLayout.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch)
tree35051494d2af230dce54d6b31c6af8fc24091316 /core/java/android/widget/RelativeLayout.java
downloadframeworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2
Initial Contribution
Diffstat (limited to 'core/java/android/widget/RelativeLayout.java')
-rw-r--r--core/java/android/widget/RelativeLayout.java950
1 files changed, 950 insertions, 0 deletions
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
new file mode 100644
index 0000000..91d5805
--- /dev/null
+++ b/core/java/android/widget/RelativeLayout.java
@@ -0,0 +1,950 @@
+/*
+ * 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.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, 0);
+ 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
+ */
+ 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
+ */
+ 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();
+ }
+ }
+
+ 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();
+ }
+ }
+
+ 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 != 0) {
+ 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 {
+ 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.
+ */
+ 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;
+ }
+ }
+}