RelativeLayout.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.util.AttributeSet;
22import android.view.View;
23import android.view.ViewGroup;
24import android.view.Gravity;
25import android.widget.RemoteViews.RemoteView;
26import android.graphics.Rect;
27import com.android.internal.R;
28
29
30/**
31 * A Layout where the positions of the children can be described in relation to each other or to the
32 * parent. For the sake of efficiency, the relations between views are evaluated in one pass, so if
33 * view Y is dependent on the position of view X, make sure the view X comes first in the layout.
34 *
35 * <p>
36 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
37 * position of its children. For example, you cannot have a RelativeLayout whose height is set to
38 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
39 * {@link #ALIGN_PARENT_BOTTOM}.
40 * </p>
41 *
42 * <p>
43 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
44 * layout attributes
45 * </p>
46 *
47 * @attr ref android.R.styleable#RelativeLayout_gravity
48 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
49 */
50@RemoteView
51public class RelativeLayout extends ViewGroup {
52    public static final int TRUE = -1;
53
54    /**
55     * Rule that aligns a child's right edge with another child's left edge.
56     */
57    public static final int LEFT_OF                  = 0;
58    /**
59     * Rule that aligns a child's left edge with another child's right edge.
60     */
61    public static final int RIGHT_OF                 = 1;
62    /**
63     * Rule that aligns a child's bottom edge with another child's top edge.
64     */
65    public static final int ABOVE                    = 2;
66    /**
67     * Rule that aligns a child's top edge with another child's bottom edge.
68     */
69    public static final int BELOW                    = 3;
70
71    /**
72     * Rule that aligns a child's baseline with another child's baseline.
73     */
74    public static final int ALIGN_BASELINE           = 4;
75    /**
76     * Rule that aligns a child's left edge with another child's left edge.
77     */
78    public static final int ALIGN_LEFT               = 5;
79    /**
80     * Rule that aligns a child's top edge with another child's top edge.
81     */
82    public static final int ALIGN_TOP                = 6;
83    /**
84     * Rule that aligns a child's right edge with another child's right edge.
85     */
86    public static final int ALIGN_RIGHT              = 7;
87    /**
88     * Rule that aligns a child's bottom edge with another child's bottom edge.
89     */
90    public static final int ALIGN_BOTTOM             = 8;
91
92    /**
93     * Rule that aligns the child's left edge with its RelativeLayout
94     * parent's left edge.
95     */
96    public static final int ALIGN_PARENT_LEFT        = 9;
97    /**
98     * Rule that aligns the child's top edge with its RelativeLayout
99     * parent's top edge.
100     */
101    public static final int ALIGN_PARENT_TOP         = 10;
102    /**
103     * Rule that aligns the child's right edge with its RelativeLayout
104     * parent's right edge.
105     */
106    public static final int ALIGN_PARENT_RIGHT       = 11;
107    /**
108     * Rule that aligns the child's bottom edge with its RelativeLayout
109     * parent's bottom edge.
110     */
111    public static final int ALIGN_PARENT_BOTTOM      = 12;
112
113    /**
114     * Rule that centers the child with respect to the bounds of its
115     * RelativeLayout parent.
116     */
117    public static final int CENTER_IN_PARENT         = 13;
118    /**
119     * Rule that centers the child horizontally with respect to the
120     * bounds of its RelativeLayout parent.
121     */
122    public static final int CENTER_HORIZONTAL        = 14;
123    /**
124     * Rule that centers the child vertically with respect to the
125     * bounds of its RelativeLayout parent.
126     */
127    public static final int CENTER_VERTICAL          = 15;
128
129    private static final int VERB_COUNT              = 16;
130
131    private View mBaselineView = null;
132    private boolean mHasBaselineAlignedChild;
133
134    private int mGravity = Gravity.LEFT | Gravity.TOP;
135    private final Rect mContentBounds = new Rect();
136    private final Rect mSelfBounds = new Rect();
137    private int mIgnoreGravity;
138
139    public RelativeLayout(Context context) {
140        super(context);
141    }
142
143    public RelativeLayout(Context context, AttributeSet attrs) {
144        super(context, attrs);
145        initFromAttributes(context, attrs);
146    }
147
148    public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
149        super(context, attrs, defStyle);
150        initFromAttributes(context, attrs);
151    }
152
153    private void initFromAttributes(Context context, AttributeSet attrs) {
154        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout);
155        mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
156        mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
157        a.recycle();
158    }
159
160    /**
161     * Defines which View is ignored when the gravity is applied. This setting has no
162     * effect if the gravity is <code>Gravity.LEFT | Gravity.TOP</code>.
163     *
164     * @param viewId The id of the View to be ignored by gravity, or 0 if no View
165     *        should be ignored.
166     *
167     * @see #setGravity(int)
168     *
169     * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
170     */
171    @android.view.RemotableViewMethod
172    public void setIgnoreGravity(int viewId) {
173        mIgnoreGravity = viewId;
174    }
175
176    /**
177     * Describes how the child views are positioned. Defaults to
178     * <code>Gravity.LEFT | Gravity.TOP</code>.
179     *
180     * @param gravity See {@link android.view.Gravity}
181     *
182     * @see #setHorizontalGravity(int)
183     * @see #setVerticalGravity(int)
184     *
185     * @attr ref android.R.styleable#RelativeLayout_gravity
186     */
187    @android.view.RemotableViewMethod
188    public void setGravity(int gravity) {
189        if (mGravity != gravity) {
190            if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
191                gravity |= Gravity.LEFT;
192            }
193
194            if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
195                gravity |= Gravity.TOP;
196            }
197
198            mGravity = gravity;
199            requestLayout();
200        }
201    }
202
203    @android.view.RemotableViewMethod
204    public void setHorizontalGravity(int horizontalGravity) {
205        final int gravity = horizontalGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
206        if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) {
207            mGravity = (mGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) | gravity;
208            requestLayout();
209        }
210    }
211
212    @android.view.RemotableViewMethod
213    public void setVerticalGravity(int verticalGravity) {
214        final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
215        if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
216            mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
217            requestLayout();
218        }
219    }
220
221    @Override
222    public int getBaseline() {
223        return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
224    }
225
226    @Override
227    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
228        int myWidth = -1;
229        int myHeight = -1;
230
231        int width = 0;
232        int height = 0;
233
234        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
235        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
236        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
237        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
238
239        // Record our dimensions if they are known;
240        if (widthMode != MeasureSpec.UNSPECIFIED) {
241            myWidth = widthSize;
242        }
243
244        if (heightMode != MeasureSpec.UNSPECIFIED) {
245            myHeight = heightSize;
246        }
247
248        if (widthMode == MeasureSpec.EXACTLY) {
249            width = myWidth;
250        }
251
252        if (heightMode == MeasureSpec.EXACTLY) {
253            height = myHeight;
254        }
255
256        int len = this.getChildCount();
257        mHasBaselineAlignedChild = false;
258
259        View ignore = null;
260        int gravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
261        final boolean horizontalGravity = gravity != Gravity.LEFT && gravity != 0;
262        gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
263        final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
264
265        int left = Integer.MAX_VALUE;
266        int top = Integer.MAX_VALUE;
267        int right = Integer.MIN_VALUE;
268        int bottom = Integer.MIN_VALUE;
269
270        if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
271            ignore = findViewById(mIgnoreGravity);
272        }
273
274        for (int i = 0; i < len; i++) {
275            View child = getChildAt(i);
276            if (child.getVisibility() != GONE) {
277                LayoutParams params = (LayoutParams) child.getLayoutParams();
278                applySizeRules(params, myWidth, myHeight);
279                measureChild(child, params, myWidth, myHeight);
280                positionChild(child, params, myWidth, myHeight);
281
282                if (widthMode != MeasureSpec.EXACTLY) {
283                    width = Math.max(width, params.mRight);
284                }
285                if (heightMode != MeasureSpec.EXACTLY) {
286                    height = Math.max(height, params.mBottom);
287                }
288
289                if (child != ignore || verticalGravity) {
290                    left = Math.min(left, params.mLeft - params.leftMargin);
291                    top = Math.min(top, params.mTop - params.topMargin);
292                }
293
294                if (child != ignore || horizontalGravity) {
295                    right = Math.max(right, params.mRight + params.rightMargin);
296                    bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
297                }
298            }
299        }
300
301        if (mHasBaselineAlignedChild) {
302            for (int i = 0; i < len; i++) {
303                View child = getChildAt(i);
304                if (child.getVisibility() != GONE) {
305                    LayoutParams params = (LayoutParams) child.getLayoutParams();
306                    alignBaseline(child, params);
307
308                    if (child != ignore || verticalGravity) {
309                    left = Math.min(left, params.mLeft - params.leftMargin);
310                    top = Math.min(top, params.mTop - params.topMargin);
311                    }
312
313                    if (child != ignore || horizontalGravity) {
314                        right = Math.max(right, params.mRight + params.rightMargin);
315                        bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
316                    }
317                }
318            }
319        }
320
321        if (widthMode != MeasureSpec.EXACTLY) {
322            // Width already has left padding in it since it was calculated by looking at
323            // the right of each child view
324            width += mPaddingRight;
325
326            if (mLayoutParams.width >= 0) {
327                width = Math.max(width, mLayoutParams.width);
328            }
329
330            width = Math.max(width, getSuggestedMinimumWidth());
331            width = resolveSize(width, widthMeasureSpec);
332        }
333        if (heightMode != MeasureSpec.EXACTLY) {
334            // Height already has top padding in it since it was calculated by looking at
335            // the bottom of each child view
336            height += mPaddingBottom;
337
338            if (mLayoutParams.height >= 0) {
339                height = Math.max(height, mLayoutParams.height);
340            }
341
342            height = Math.max(height, getSuggestedMinimumHeight());
343            height = resolveSize(height, heightMeasureSpec);
344        }
345
346        if (horizontalGravity || verticalGravity) {
347            final Rect selfBounds = mSelfBounds;
348            selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
349                    height - mPaddingBottom);
350
351            final Rect contentBounds = mContentBounds;
352            Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds);
353
354            final int horizontalOffset = contentBounds.left - left;
355            final int verticalOffset = contentBounds.top - top;
356            if (horizontalOffset != 0 || verticalOffset != 0) {
357                for (int i = 0; i < len; i++) {
358                    View child = getChildAt(i);
359                    if (child.getVisibility() != GONE && child != ignore) {
360                        LayoutParams params = (LayoutParams) child.getLayoutParams();
361                        params.mLeft += horizontalOffset;
362                        params.mRight += horizontalOffset;
363                        params.mTop += verticalOffset;
364                        params.mBottom += verticalOffset;
365                    }
366                }
367            }
368        }
369
370        setMeasuredDimension(width, height);
371    }
372
373    private void alignBaseline(View child, LayoutParams params) {
374        int[] rules = params.getRules();
375        int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE);
376
377        if (anchorBaseline != -1) {
378            LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE);
379            if (anchorParams != null) {
380                int offset = anchorParams.mTop + anchorBaseline;
381                int baseline = child.getBaseline();
382                if (baseline != -1) {
383                    offset -= baseline;
384                }
385                int height = params.mBottom - params.mTop;
386                params.mTop = offset;
387                params.mBottom = params.mTop + height;
388            }
389        }
390
391        if (mBaselineView == null) {
392            mBaselineView = child;
393        } else {
394            LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams();
395            if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) {
396                mBaselineView = child;
397            }
398        }
399    }
400
401    /**
402     * Measure a child. The child should have left, top, right and bottom information
403     * stored in its LayoutParams. If any of these values is -1 it means that the view
404     * can extend up to the corresponding edge.
405     *
406     * @param child Child to measure
407     * @param params LayoutParams associated with child
408     * @param myWidth Width of the the RelativeLayout
409     * @param myHeight Height of the RelativeLayout
410     */
411    private void measureChild(View child, LayoutParams params, int myWidth,
412            int myHeight) {
413
414        int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
415                params.mRight, params.width,
416                params.leftMargin, params.rightMargin,
417                mPaddingLeft, mPaddingRight,
418                myWidth);
419        int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
420                params.mBottom, params.height,
421                params.topMargin, params.bottomMargin,
422                mPaddingTop, mPaddingBottom,
423                myHeight);
424        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
425    }
426
427    /**
428     * Get a measure spec that accounts for all of the constraints on this view.
429     * This includes size contstraints imposed by the RelativeLayout as well as
430     * the View's desired dimension.
431     *
432     * @param childStart The left or top field of the child's layout params
433     * @param childEnd The right or bottom field of the child's layout params
434     * @param childSize The child's desired size (the width or height field of
435     *        the child's layout params)
436     * @param startMargin The left or top margin
437     * @param endMargin The right or bottom margin
438     * @param startPadding mPaddingLeft or mPaddingTop
439     * @param endPadding mPaddingRight or mPaddingBottom
440     * @param mySize The width or height of this view (the RelativeLayout)
441     * @return MeasureSpec for the child
442     */
443    private int getChildMeasureSpec(int childStart, int childEnd,
444            int childSize, int startMargin, int endMargin, int startPadding,
445            int endPadding, int mySize) {
446        int childSpecMode = 0;
447        int childSpecSize = 0;
448
449        // Figure out start and end bounds.
450        int tempStart = childStart;
451        int tempEnd = childEnd;
452
453        // If the view did not express a layout constraint for an edge, use
454        // view's margins and our padding
455        if (tempStart < 0) {
456            tempStart = startPadding + startMargin;
457        }
458        if (tempEnd < 0) {
459            tempEnd = mySize - endPadding - endMargin;
460        }
461
462        // Figure out maximum size available to this view
463        int maxAvailable = tempEnd - tempStart;
464
465        if (childStart >= 0 && childEnd >= 0) {
466            // Constraints fixed both edges, so child must be an exact size
467            childSpecMode = MeasureSpec.EXACTLY;
468            childSpecSize = maxAvailable;
469        } else {
470            if (childSize >= 0) {
471                // Child wanted an exact size. Give as much as possible
472                childSpecMode = MeasureSpec.EXACTLY;
473
474                if (maxAvailable >= 0) {
475                    // We have a maxmum size in this dimension.
476                    childSpecSize = Math.min(maxAvailable, childSize);
477                } else {
478                    // We can grow in this dimension.
479                    childSpecSize = childSize;
480                }
481            } else if (childSize == LayoutParams.FILL_PARENT) {
482                // Child wanted to be as big as possible. Give all availble
483                // space
484                childSpecMode = MeasureSpec.EXACTLY;
485                childSpecSize = maxAvailable;
486            } else if (childSize == LayoutParams.WRAP_CONTENT) {
487                // Child wants to wrap content. Use AT_MOST
488                // to communicate available space if we know
489                // our max size
490                if (maxAvailable >= 0) {
491                    // We have a maxmum size in this dimension.
492                    childSpecMode = MeasureSpec.AT_MOST;
493                    childSpecSize = maxAvailable;
494                } else {
495                    // We can grow in this dimension. Child can be as big as it
496                    // wants
497                    childSpecMode = MeasureSpec.UNSPECIFIED;
498                    childSpecSize = 0;
499                }
500            }
501        }
502
503        return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
504    }
505
506    /**
507     * After the child has been measured, assign it a position. Some views may
508     * already have final values for l,t,r,b. Others may have one or both edges
509     * unfixed (i.e. set to -1) in each dimension. These will get positioned
510     * based on which edge is fixed, the view's desired dimension, and whether
511     * or not it is centered.
512     *
513     * @param child Child to position
514     * @param params LayoutParams associated with child
515     * @param myWidth Width of the the RelativeLayout
516     * @param myHeight Height of the RelativeLayout
517     */
518    private void positionChild(View child, LayoutParams params, int myWidth, int myHeight) {
519        int[] rules = params.getRules();
520
521        if (params.mLeft < 0 && params.mRight >= 0) {
522            // Right is fixed, but left varies
523            params.mLeft = params.mRight - child.getMeasuredWidth();
524        } else if (params.mLeft >= 0 && params.mRight < 0) {
525            // Left is fixed, but right varies
526            params.mRight = params.mLeft + child.getMeasuredWidth();
527        } else if (params.mLeft < 0 && params.mRight < 0) {
528            // Both left and right vary
529            if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) {
530                centerHorizontal(child, params, myWidth);
531            } else {
532                params.mLeft = mPaddingLeft + params.leftMargin;
533                params.mRight = params.mLeft + child.getMeasuredWidth();
534            }
535        }
536
537        if (params.mTop < 0 && params.mBottom >= 0) {
538            // Bottom is fixed, but top varies
539            params.mTop = params.mBottom - child.getMeasuredHeight();
540        } else if (params.mTop >= 0 && params.mBottom < 0) {
541            // Top is fixed, but bottom varies
542            params.mBottom = params.mTop + child.getMeasuredHeight();
543        } else if (params.mTop < 0 && params.mBottom < 0) {
544            // Both top and bottom vary
545            if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) {
546                centerVertical(child, params, myHeight);
547            } else {
548                params.mTop = mPaddingTop + params.topMargin;
549                params.mBottom = params.mTop + child.getMeasuredHeight();
550            }
551        }
552    }
553
554    /**
555     * Set l,t,r,b values in the LayoutParams for one view based on its layout rules.
556     * Big assumption #1: All antecedents of this view have been sized & positioned
557     * Big assumption #2: The dimensions of the parent view (the RelativeLayout)
558     * are already known if they are needed.
559     *
560     * @param childParams LayoutParams for the view being positioned
561     * @param myWidth Width of the the RelativeLayout
562     * @param myHeight Height of the RelativeLayout
563     */
564    private void applySizeRules(LayoutParams childParams, int myWidth, int myHeight) {
565        int[] rules = childParams.getRules();
566        RelativeLayout.LayoutParams anchorParams;
567
568        // -1 indicated a "soft requirement" in that direction. For example:
569        // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right
570        // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left
571        // left=10, right=20 means the left and right ends are both fixed
572        childParams.mLeft = -1;
573        childParams.mRight = -1;
574
575        anchorParams = getRelatedViewParams(rules, LEFT_OF);
576        if (anchorParams != null) {
577            childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
578                    childParams.rightMargin);
579        } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
580            if (myWidth >= 0) {
581                childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
582            } else {
583                // FIXME uh oh...
584            }
585        }
586
587        anchorParams = getRelatedViewParams(rules, RIGHT_OF);
588        if (anchorParams != null) {
589            childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
590                    childParams.leftMargin);
591        } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
592            childParams.mLeft = mPaddingLeft + childParams.leftMargin;
593        }
594
595        anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
596        if (anchorParams != null) {
597            childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
598        } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
599            childParams.mLeft = mPaddingLeft + childParams.leftMargin;
600        }
601
602        anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
603        if (anchorParams != null) {
604            childParams.mRight = anchorParams.mRight - childParams.rightMargin;
605        } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
606            if (myWidth >= 0) {
607                childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
608            } else {
609                // FIXME uh oh...
610            }
611        }
612
613        if (0 != rules[ALIGN_PARENT_LEFT]) {
614            childParams.mLeft = mPaddingLeft + childParams.leftMargin;
615        }
616
617        if (0 != rules[ALIGN_PARENT_RIGHT]) {
618            if (myWidth >= 0) {
619                childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
620            } else {
621                // FIXME uh oh...
622            }
623        }
624
625        childParams.mTop = -1;
626        childParams.mBottom = -1;
627
628        anchorParams = getRelatedViewParams(rules, ABOVE);
629        if (anchorParams != null) {
630            childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
631                    childParams.bottomMargin);
632        } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
633            if (myHeight >= 0) {
634                childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
635            } else {
636                // FIXME uh oh...
637            }
638        }
639
640        anchorParams = getRelatedViewParams(rules, BELOW);
641        if (anchorParams != null) {
642            childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
643                    childParams.topMargin);
644        } else if (childParams.alignWithParent && rules[BELOW] != 0) {
645            childParams.mTop = mPaddingTop + childParams.topMargin;
646        }
647
648        anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
649        if (anchorParams != null) {
650            childParams.mTop = anchorParams.mTop + childParams.topMargin;
651        } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
652            childParams.mTop = mPaddingTop + childParams.topMargin;
653        }
654
655        anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
656        if (anchorParams != null) {
657            childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
658        } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
659            if (myHeight >= 0) {
660                childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
661            } else {
662                // FIXME uh oh...
663            }
664        }
665
666        if (0 != rules[ALIGN_PARENT_TOP]) {
667            childParams.mTop = mPaddingTop + childParams.topMargin;
668        }
669
670        if (0 != rules[ALIGN_PARENT_BOTTOM]) {
671            if (myHeight >= 0) {
672                childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
673            } else {
674                // FIXME uh oh...
675            }
676        }
677
678        if (rules[ALIGN_BASELINE] != 0) {
679            mHasBaselineAlignedChild = true;
680        }
681    }
682
683    private View getRelatedView(int[] rules, int relation) {
684        int id = rules[relation];
685        if (id != 0) {
686            View v = findViewById(id);
687            if (v == null) {
688                return null;
689            }
690
691            // Find the first non-GONE view up the chain
692            while (v.getVisibility() == View.GONE) {
693                rules = ((LayoutParams) v.getLayoutParams()).getRules();
694                v = v.findViewById(rules[relation]);
695                if (v == null) {
696                    return null;
697                }
698            }
699
700            return v;
701        }
702
703        return null;
704    }
705
706    private LayoutParams getRelatedViewParams(int[] rules, int relation) {
707        View v = getRelatedView(rules, relation);
708        if (v != null) {
709            ViewGroup.LayoutParams params = v.getLayoutParams();
710            if (params instanceof LayoutParams) {
711                return (LayoutParams) v.getLayoutParams();
712            }
713        }
714        return null;
715    }
716
717    private int getRelatedViewBaseline(int[] rules, int relation) {
718        View v = getRelatedView(rules, relation);
719        if (v != null) {
720            return v.getBaseline();
721        }
722        return -1;
723    }
724
725    private void centerHorizontal(View child, LayoutParams params, int myWidth) {
726        int childWidth = child.getMeasuredWidth();
727        int left = (myWidth - childWidth) / 2;
728
729        params.mLeft = left;
730        params.mRight = left + childWidth;
731    }
732
733    private void centerVertical(View child, LayoutParams params, int myHeight) {
734        int childHeight = child.getMeasuredHeight();
735        int top = (myHeight - childHeight) / 2;
736
737        params.mTop = top;
738        params.mBottom = top + childHeight;
739    }
740
741    @Override
742    protected void onLayout(boolean changed, int l, int t, int r, int b) {
743        //  The layout has actually already been performed and the positions
744        //  cached.  Apply the cached values to the children.
745        int count = getChildCount();
746
747        for (int i = 0; i < count; i++) {
748            View child = getChildAt(i);
749            if (child.getVisibility() != GONE) {
750                RelativeLayout.LayoutParams st =
751                        (RelativeLayout.LayoutParams) child.getLayoutParams();
752                child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
753
754            }
755        }
756    }
757
758    @Override
759    public LayoutParams generateLayoutParams(AttributeSet attrs) {
760        return new RelativeLayout.LayoutParams(getContext(), attrs);
761    }
762
763    /**
764     * Returns a set of layout parameters with a width of
765     * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
766     * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
767     */
768    @Override
769    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
770        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
771    }
772
773    // Override to allow type-checking of LayoutParams.
774    @Override
775    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
776        return p instanceof RelativeLayout.LayoutParams;
777    }
778
779    @Override
780    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
781        return new LayoutParams(p);
782    }
783
784    /**
785     * Per-child layout information associated with RelativeLayout.
786     *
787     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
788     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
789     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
790     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
791     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
792     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
793     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
794     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
795     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
796     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
797     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
798     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
799     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
800     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
801     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
802     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
803     * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
804     */
805    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
806        private int[] mRules = new int[VERB_COUNT];
807        private int mLeft, mTop, mRight, mBottom;
808
809        /**
810         * When true, uses the parent as the anchor if the anchor doesn't exist or if
811         * the anchor's visibility is GONE.
812         */
813        public boolean alignWithParent;
814
815        public LayoutParams(Context c, AttributeSet attrs) {
816            super(c, attrs);
817
818            TypedArray a = c.obtainStyledAttributes(attrs,
819                    com.android.internal.R.styleable.RelativeLayout_Layout);
820
821            final int[] rules = mRules;
822
823            final int N = a.getIndexCount();
824            for (int i = 0; i < N; i++) {
825                int attr = a.getIndex(i);
826                switch (attr) {
827                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
828                        alignWithParent = a.getBoolean(attr, false);
829                        break;
830                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
831                        rules[LEFT_OF] = a.getResourceId(attr, 0);
832                        break;
833                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
834                        rules[RIGHT_OF] = a.getResourceId(attr, 0);
835                        break;
836                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
837                        rules[ABOVE] = a.getResourceId(attr, 0);
838                        break;
839                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
840                        rules[BELOW] = a.getResourceId(attr, 0);
841                        break;
842                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
843                        rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
844                        break;
845                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
846                        rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
847                        break;
848                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
849                        rules[ALIGN_TOP] = a.getResourceId(attr, 0);
850                        break;
851                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
852                        rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
853                        break;
854                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
855                        rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
856                        break;
857                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
858                        rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
859                        break;
860                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
861                        rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
862                        break;
863                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
864                        rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
865                        break;
866                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
867                        rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
868                        break;
869                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
870                        rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
871                        break;
872                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
873                        rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
874                        break;
875                    case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
876                        rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
877                       break;
878                }
879            }
880
881            a.recycle();
882        }
883
884        public LayoutParams(int w, int h) {
885            super(w, h);
886        }
887
888        /**
889         * {@inheritDoc}
890         */
891        public LayoutParams(ViewGroup.LayoutParams source) {
892            super(source);
893        }
894
895        /**
896         * {@inheritDoc}
897         */
898        public LayoutParams(ViewGroup.MarginLayoutParams source) {
899            super(source);
900        }
901
902        @Override
903        public String debug(String output) {
904            return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
905                    ", height=" + sizeToString(height) + " }";
906        }
907
908        /**
909         * Adds a layout rule to be interpreted by the RelativeLayout. This
910         * method should only be used for constraints that don't refer to another sibling
911         * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE}
912         * for true or - for false). To specify a verb that takes a subject, use
913         * {@link #addRule(int, int)} instead.
914         *
915         * @param verb One of the verbs defined by
916         *        {@link android.widget.RelativeLayout RelativeLayout}, such as
917         *        ALIGN_WITH_PARENT_LEFT.
918         * @see #addRule(int, int)
919         */
920        public void addRule(int verb) {
921            mRules[verb] = TRUE;
922        }
923
924        /**
925         * Adds a layout rule to be interpreted by the RelativeLayout. Use this for
926         * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
927         * value (VISIBLE).
928         *
929         * @param verb One of the verbs defined by
930         *        {@link android.widget.RelativeLayout RelativeLayout}, such as
931         *         ALIGN_WITH_PARENT_LEFT.
932         * @param anchor The id of another view to use as an anchor,
933         *        or a boolean value(represented as {@link RelativeLayout#TRUE})
934         *        for true or 0 for false).  For verbs that don't refer to another sibling
935         *        (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
936         * @see #addRule(int)
937         */
938        public void addRule(int verb, int anchor) {
939            mRules[verb] = anchor;
940        }
941
942        /**
943         * Retrieves a complete list of all supported rules, where the index is the rule
944         * verb, and the element value is the value specified, or "false" if it was never
945         * set.
946         *
947         * @return the supported rules
948         * @see #addRule(int, int)
949         */
950        public int[] getRules() {
951            return mRules;
952        }
953    }
954}
955