• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.widget;
18 
19 import java.util.ArrayList;
20 
21 import android.content.Context;
22 import android.content.res.TypedArray;
23 import android.graphics.Canvas;
24 import android.graphics.Rect;
25 import android.graphics.Region;
26 import android.graphics.drawable.Drawable;
27 import android.util.AttributeSet;
28 import android.view.Gravity;
29 import android.view.View;
30 import android.view.ViewDebug;
31 import android.view.ViewGroup;
32 import android.widget.RemoteViews.RemoteView;
33 
34 
35 /**
36  * FrameLayout is designed to block out an area on the screen to display
37  * a single item. Generally, FrameLayout should be used to hold a single child view, because it can
38  * be difficult to organize child views in a way that's scalable to different screen sizes without
39  * the children overlapping each other. You can, however, add multiple children to a FrameLayout
40  * and control their position within the FrameLayout by assigning gravity to each child, using the
41  * <a href="FrameLayout.LayoutParams.html#attr_android:layout_gravity">{@code
42  * android:layout_gravity}</a> attribute.
43  * <p>Child views are drawn in a stack, with the most recently added child on top.
44  * The size of the FrameLayout is the size of its largest child (plus padding), visible
45  * or not (if the FrameLayout's parent permits). Views that are {@link android.view.View#GONE} are
46  * used for sizing
47  * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
48  * is set to true.
49  *
50  * @attr ref android.R.styleable#FrameLayout_foreground
51  * @attr ref android.R.styleable#FrameLayout_foregroundGravity
52  * @attr ref android.R.styleable#FrameLayout_measureAllChildren
53  */
54 @RemoteView
55 public class FrameLayout extends ViewGroup {
56     private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.LEFT;
57 
58     @ViewDebug.ExportedProperty(category = "measurement")
59     boolean mMeasureAllChildren = false;
60 
61     @ViewDebug.ExportedProperty(category = "drawing")
62     private Drawable mForeground;
63 
64     @ViewDebug.ExportedProperty(category = "padding")
65     private int mForegroundPaddingLeft = 0;
66 
67     @ViewDebug.ExportedProperty(category = "padding")
68     private int mForegroundPaddingTop = 0;
69 
70     @ViewDebug.ExportedProperty(category = "padding")
71     private int mForegroundPaddingRight = 0;
72 
73     @ViewDebug.ExportedProperty(category = "padding")
74     private int mForegroundPaddingBottom = 0;
75 
76     private final Rect mSelfBounds = new Rect();
77     private final Rect mOverlayBounds = new Rect();
78 
79     @ViewDebug.ExportedProperty(category = "drawing")
80     private int mForegroundGravity = Gravity.FILL;
81 
82     /** {@hide} */
83     @ViewDebug.ExportedProperty(category = "drawing")
84     protected boolean mForegroundInPadding = true;
85 
86     boolean mForegroundBoundsChanged = false;
87 
88     private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);
89 
FrameLayout(Context context)90     public FrameLayout(Context context) {
91         super(context);
92     }
93 
FrameLayout(Context context, AttributeSet attrs)94     public FrameLayout(Context context, AttributeSet attrs) {
95         this(context, attrs, 0);
96     }
97 
FrameLayout(Context context, AttributeSet attrs, int defStyle)98     public FrameLayout(Context context, AttributeSet attrs, int defStyle) {
99         super(context, attrs, defStyle);
100 
101         TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout,
102                     defStyle, 0);
103 
104         mForegroundGravity = a.getInt(
105                 com.android.internal.R.styleable.FrameLayout_foregroundGravity, mForegroundGravity);
106 
107         final Drawable d = a.getDrawable(com.android.internal.R.styleable.FrameLayout_foreground);
108         if (d != null) {
109             setForeground(d);
110         }
111 
112         if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) {
113             setMeasureAllChildren(true);
114         }
115 
116         mForegroundInPadding = a.getBoolean(
117                 com.android.internal.R.styleable.FrameLayout_foregroundInsidePadding, true);
118 
119         a.recycle();
120     }
121 
122     /**
123      * Describes how the foreground is positioned. Defaults to START and TOP.
124      *
125      * @param foregroundGravity See {@link android.view.Gravity}
126      *
127      * @attr ref android.R.styleable#FrameLayout_foregroundGravity
128      */
129     @android.view.RemotableViewMethod
setForegroundGravity(int foregroundGravity)130     public void setForegroundGravity(int foregroundGravity) {
131         if (mForegroundGravity != foregroundGravity) {
132             if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
133                 foregroundGravity |= Gravity.START;
134             }
135 
136             if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
137                 foregroundGravity |= Gravity.TOP;
138             }
139 
140             mForegroundGravity = foregroundGravity;
141 
142 
143             if (mForegroundGravity == Gravity.FILL && mForeground != null) {
144                 Rect padding = new Rect();
145                 if (mForeground.getPadding(padding)) {
146                     mForegroundPaddingLeft = padding.left;
147                     mForegroundPaddingTop = padding.top;
148                     mForegroundPaddingRight = padding.right;
149                     mForegroundPaddingBottom = padding.bottom;
150                 }
151             } else {
152                 mForegroundPaddingLeft = 0;
153                 mForegroundPaddingTop = 0;
154                 mForegroundPaddingRight = 0;
155                 mForegroundPaddingBottom = 0;
156             }
157 
158             requestLayout();
159         }
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     @Override
verifyDrawable(Drawable who)166     protected boolean verifyDrawable(Drawable who) {
167         return super.verifyDrawable(who) || (who == mForeground);
168     }
169 
170     @Override
jumpDrawablesToCurrentState()171     public void jumpDrawablesToCurrentState() {
172         super.jumpDrawablesToCurrentState();
173         if (mForeground != null) mForeground.jumpToCurrentState();
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
179     @Override
drawableStateChanged()180     protected void drawableStateChanged() {
181         super.drawableStateChanged();
182         if (mForeground != null && mForeground.isStateful()) {
183             mForeground.setState(getDrawableState());
184         }
185     }
186 
187     /**
188      * Returns a set of layout parameters with a width of
189      * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
190      * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}.
191      */
192     @Override
generateDefaultLayoutParams()193     protected LayoutParams generateDefaultLayoutParams() {
194         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
195     }
196 
197     /**
198      * Supply a Drawable that is to be rendered on top of all of the child
199      * views in the frame layout.  Any padding in the Drawable will be taken
200      * into account by ensuring that the children are inset to be placed
201      * inside of the padding area.
202      *
203      * @param drawable The Drawable to be drawn on top of the children.
204      *
205      * @attr ref android.R.styleable#FrameLayout_foreground
206      */
setForeground(Drawable drawable)207     public void setForeground(Drawable drawable) {
208         if (mForeground != drawable) {
209             if (mForeground != null) {
210                 mForeground.setCallback(null);
211                 unscheduleDrawable(mForeground);
212             }
213 
214             mForeground = drawable;
215             mForegroundPaddingLeft = 0;
216             mForegroundPaddingTop = 0;
217             mForegroundPaddingRight = 0;
218             mForegroundPaddingBottom = 0;
219 
220             if (drawable != null) {
221                 setWillNotDraw(false);
222                 drawable.setCallback(this);
223                 if (drawable.isStateful()) {
224                     drawable.setState(getDrawableState());
225                 }
226                 if (mForegroundGravity == Gravity.FILL) {
227                     Rect padding = new Rect();
228                     if (drawable.getPadding(padding)) {
229                         mForegroundPaddingLeft = padding.left;
230                         mForegroundPaddingTop = padding.top;
231                         mForegroundPaddingRight = padding.right;
232                         mForegroundPaddingBottom = padding.bottom;
233                     }
234                 }
235             }  else {
236                 setWillNotDraw(true);
237             }
238             requestLayout();
239             invalidate();
240         }
241     }
242 
243     /**
244      * Returns the drawable used as the foreground of this FrameLayout. The
245      * foreground drawable, if non-null, is always drawn on top of the children.
246      *
247      * @return A Drawable or null if no foreground was set.
248      */
getForeground()249     public Drawable getForeground() {
250         return mForeground;
251     }
252 
getPaddingLeftWithForeground()253     private int getPaddingLeftWithForeground() {
254         return mForegroundInPadding ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
255             mPaddingLeft + mForegroundPaddingLeft;
256     }
257 
getPaddingRightWithForeground()258     private int getPaddingRightWithForeground() {
259         return mForegroundInPadding ? Math.max(mPaddingRight, mForegroundPaddingRight) :
260             mPaddingRight + mForegroundPaddingRight;
261     }
262 
getPaddingTopWithForeground()263     private int getPaddingTopWithForeground() {
264         return mForegroundInPadding ? Math.max(mPaddingTop, mForegroundPaddingTop) :
265             mPaddingTop + mForegroundPaddingTop;
266     }
267 
getPaddingBottomWithForeground()268     private int getPaddingBottomWithForeground() {
269         return mForegroundInPadding ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
270             mPaddingBottom + mForegroundPaddingBottom;
271     }
272 
273 
274     /**
275      * {@inheritDoc}
276      */
277     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)278     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
279         int count = getChildCount();
280 
281         final boolean measureMatchParentChildren =
282                 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
283                 MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
284         mMatchParentChildren.clear();
285 
286         int maxHeight = 0;
287         int maxWidth = 0;
288         int childState = 0;
289 
290         for (int i = 0; i < count; i++) {
291             final View child = getChildAt(i);
292             if (mMeasureAllChildren || child.getVisibility() != GONE) {
293                 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
294                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
295                 maxWidth = Math.max(maxWidth,
296                         child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
297                 maxHeight = Math.max(maxHeight,
298                         child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
299                 childState = combineMeasuredStates(childState, child.getMeasuredState());
300                 if (measureMatchParentChildren) {
301                     if (lp.width == LayoutParams.MATCH_PARENT ||
302                             lp.height == LayoutParams.MATCH_PARENT) {
303                         mMatchParentChildren.add(child);
304                     }
305                 }
306             }
307         }
308 
309         // Account for padding too
310         maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
311         maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
312 
313         // Check against our minimum height and width
314         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
315         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
316 
317         // Check against our foreground's minimum height and width
318         final Drawable drawable = getForeground();
319         if (drawable != null) {
320             maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
321             maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
322         }
323 
324         setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
325                 resolveSizeAndState(maxHeight, heightMeasureSpec,
326                         childState << MEASURED_HEIGHT_STATE_SHIFT));
327 
328         count = mMatchParentChildren.size();
329         if (count > 1) {
330             for (int i = 0; i < count; i++) {
331                 final View child = mMatchParentChildren.get(i);
332 
333                 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
334                 int childWidthMeasureSpec;
335                 int childHeightMeasureSpec;
336 
337                 if (lp.width == LayoutParams.MATCH_PARENT) {
338                     childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
339                             getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
340                             lp.leftMargin - lp.rightMargin,
341                             MeasureSpec.EXACTLY);
342                 } else {
343                     childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
344                             getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
345                             lp.leftMargin + lp.rightMargin,
346                             lp.width);
347                 }
348 
349                 if (lp.height == LayoutParams.MATCH_PARENT) {
350                     childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
351                             getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
352                             lp.topMargin - lp.bottomMargin,
353                             MeasureSpec.EXACTLY);
354                 } else {
355                     childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
356                             getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
357                             lp.topMargin + lp.bottomMargin,
358                             lp.height);
359                 }
360 
361                 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
362             }
363         }
364     }
365 
366     /**
367      * {@inheritDoc}
368      */
369     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)370     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
371         final int count = getChildCount();
372 
373         final int parentLeft = getPaddingLeftWithForeground();
374         final int parentRight = right - left - getPaddingRightWithForeground();
375 
376         final int parentTop = getPaddingTopWithForeground();
377         final int parentBottom = bottom - top - getPaddingBottomWithForeground();
378 
379         mForegroundBoundsChanged = true;
380 
381         for (int i = 0; i < count; i++) {
382             final View child = getChildAt(i);
383             if (child.getVisibility() != GONE) {
384                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
385 
386                 final int width = child.getMeasuredWidth();
387                 final int height = child.getMeasuredHeight();
388 
389                 int childLeft;
390                 int childTop;
391 
392                 int gravity = lp.gravity;
393                 if (gravity == -1) {
394                     gravity = DEFAULT_CHILD_GRAVITY;
395                 }
396 
397                 final int layoutDirection = getResolvedLayoutDirection();
398                 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
399                 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
400 
401                 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
402                     case Gravity.LEFT:
403                         childLeft = parentLeft + lp.leftMargin;
404                         break;
405                     case Gravity.CENTER_HORIZONTAL:
406                         childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
407                         lp.leftMargin - lp.rightMargin;
408                         break;
409                     case Gravity.RIGHT:
410                         childLeft = parentRight - width - lp.rightMargin;
411                         break;
412                     default:
413                         childLeft = parentLeft + lp.leftMargin;
414                 }
415 
416                 switch (verticalGravity) {
417                     case Gravity.TOP:
418                         childTop = parentTop + lp.topMargin;
419                         break;
420                     case Gravity.CENTER_VERTICAL:
421                         childTop = parentTop + (parentBottom - parentTop - height) / 2 +
422                         lp.topMargin - lp.bottomMargin;
423                         break;
424                     case Gravity.BOTTOM:
425                         childTop = parentBottom - height - lp.bottomMargin;
426                         break;
427                     default:
428                         childTop = parentTop + lp.topMargin;
429                 }
430 
431                 child.layout(childLeft, childTop, childLeft + width, childTop + height);
432             }
433         }
434     }
435 
436     /**
437      * {@inheritDoc}
438      */
439     @Override
onSizeChanged(int w, int h, int oldw, int oldh)440     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
441         super.onSizeChanged(w, h, oldw, oldh);
442         mForegroundBoundsChanged = true;
443     }
444 
445     /**
446      * {@inheritDoc}
447      */
448     @Override
draw(Canvas canvas)449     public void draw(Canvas canvas) {
450         super.draw(canvas);
451 
452         if (mForeground != null) {
453             final Drawable foreground = mForeground;
454 
455             if (mForegroundBoundsChanged) {
456                 mForegroundBoundsChanged = false;
457                 final Rect selfBounds = mSelfBounds;
458                 final Rect overlayBounds = mOverlayBounds;
459 
460                 final int w = mRight-mLeft;
461                 final int h = mBottom-mTop;
462 
463                 if (mForegroundInPadding) {
464                     selfBounds.set(0, 0, w, h);
465                 } else {
466                     selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
467                 }
468 
469                 final int layoutDirection = getResolvedLayoutDirection();
470                 Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
471                         foreground.getIntrinsicHeight(), selfBounds, overlayBounds,
472                         layoutDirection);
473                 foreground.setBounds(overlayBounds);
474             }
475 
476             foreground.draw(canvas);
477         }
478     }
479 
480     /**
481      * {@inheritDoc}
482      */
483     @Override
gatherTransparentRegion(Region region)484     public boolean gatherTransparentRegion(Region region) {
485         boolean opaque = super.gatherTransparentRegion(region);
486         if (region != null && mForeground != null) {
487             applyDrawableToTransparentRegion(mForeground, region);
488         }
489         return opaque;
490     }
491 
492     /**
493      * Sets whether to consider all children, or just those in
494      * the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
495      *
496      * @param measureAll true to consider children marked GONE, false otherwise.
497      * Default value is false.
498      *
499      * @attr ref android.R.styleable#FrameLayout_measureAllChildren
500      */
501     @android.view.RemotableViewMethod
setMeasureAllChildren(boolean measureAll)502     public void setMeasureAllChildren(boolean measureAll) {
503         mMeasureAllChildren = measureAll;
504     }
505 
506     /**
507      * Determines whether all children, or just those in the VISIBLE or
508      * INVISIBLE state, are considered when measuring.
509      *
510      * @return Whether all children are considered when measuring.
511      *
512      * @deprecated This method is deprecated in favor of
513      * {@link #getMeasureAllChildren() getMeasureAllChildren()}, which was
514      * renamed for consistency with
515      * {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}.
516      */
517     @Deprecated
getConsiderGoneChildrenWhenMeasuring()518     public boolean getConsiderGoneChildrenWhenMeasuring() {
519         return getMeasureAllChildren();
520     }
521 
522     /**
523      * Determines whether all children, or just those in the VISIBLE or
524      * INVISIBLE state, are considered when measuring.
525      *
526      * @return Whether all children are considered when measuring.
527      */
getMeasureAllChildren()528     public boolean getMeasureAllChildren() {
529         return mMeasureAllChildren;
530     }
531 
532     /**
533      * {@inheritDoc}
534      */
535     @Override
generateLayoutParams(AttributeSet attrs)536     public LayoutParams generateLayoutParams(AttributeSet attrs) {
537         return new FrameLayout.LayoutParams(getContext(), attrs);
538     }
539 
540     @Override
shouldDelayChildPressedState()541     public boolean shouldDelayChildPressedState() {
542         return false;
543     }
544 
545     /**
546      * {@inheritDoc}
547      */
548     @Override
checkLayoutParams(ViewGroup.LayoutParams p)549     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
550         return p instanceof LayoutParams;
551     }
552 
553     @Override
generateLayoutParams(ViewGroup.LayoutParams p)554     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
555         return new LayoutParams(p);
556     }
557 
558     /**
559      * Per-child layout information for layouts that support margins.
560      * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
561      * for a list of all child view attributes that this class supports.
562      *
563      * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
564      */
565     public static class LayoutParams extends MarginLayoutParams {
566         /**
567          * The gravity to apply with the View to which these layout parameters
568          * are associated.
569          *
570          * @see android.view.Gravity
571          *
572          * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
573          */
574         public int gravity = -1;
575 
576         /**
577          * {@inheritDoc}
578          */
LayoutParams(Context c, AttributeSet attrs)579         public LayoutParams(Context c, AttributeSet attrs) {
580             super(c, attrs);
581 
582             TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout);
583             gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1);
584             a.recycle();
585         }
586 
587         /**
588          * {@inheritDoc}
589          */
LayoutParams(int width, int height)590         public LayoutParams(int width, int height) {
591             super(width, height);
592         }
593 
594         /**
595          * Creates a new set of layout parameters with the specified width, height
596          * and weight.
597          *
598          * @param width the width, either {@link #MATCH_PARENT},
599          *        {@link #WRAP_CONTENT} or a fixed size in pixels
600          * @param height the height, either {@link #MATCH_PARENT},
601          *        {@link #WRAP_CONTENT} or a fixed size in pixels
602          * @param gravity the gravity
603          *
604          * @see android.view.Gravity
605          */
LayoutParams(int width, int height, int gravity)606         public LayoutParams(int width, int height, int gravity) {
607             super(width, height);
608             this.gravity = gravity;
609         }
610 
611         /**
612          * {@inheritDoc}
613          */
LayoutParams(ViewGroup.LayoutParams source)614         public LayoutParams(ViewGroup.LayoutParams source) {
615             super(source);
616         }
617 
618         /**
619          * {@inheritDoc}
620          */
LayoutParams(ViewGroup.MarginLayoutParams source)621         public LayoutParams(ViewGroup.MarginLayoutParams source) {
622             super(source);
623         }
624     }
625 }
626