• 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 android.content.Context;
20 import android.content.res.TypedArray;
21 import android.graphics.Canvas;
22 import android.graphics.Rect;
23 import android.graphics.Region;
24 import android.graphics.drawable.Drawable;
25 import android.util.AttributeSet;
26 import android.view.View;
27 import android.view.ViewDebug;
28 import android.view.ViewGroup;
29 import android.view.Gravity;
30 import android.widget.RemoteViews.RemoteView;
31 
32 
33 /**
34  * FrameLayout is designed to block out an area on the screen to display
35  * a single item. You can add multiple children to a FrameLayout, but all
36  * children are pegged to the top left of the screen.
37  * Children are drawn in a stack, with the most recently added child on top.
38  * The size of the frame layout is the size of its largest child (plus padding), visible
39  * or not (if the FrameLayout's parent permits). Views that are GONE are used for sizing
40  * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
41  * is set to true.
42  *
43  * @attr ref android.R.styleable#FrameLayout_foreground
44  * @attr ref android.R.styleable#FrameLayout_foregroundGravity
45  * @attr ref android.R.styleable#FrameLayout_measureAllChildren
46  */
47 @RemoteView
48 public class FrameLayout extends ViewGroup {
49     @ViewDebug.ExportedProperty(category = "measurement")
50     boolean mMeasureAllChildren = false;
51 
52     @ViewDebug.ExportedProperty(category = "drawing")
53     private Drawable mForeground;
54 
55     @ViewDebug.ExportedProperty(category = "padding")
56     private int mForegroundPaddingLeft = 0;
57 
58     @ViewDebug.ExportedProperty(category = "padding")
59     private int mForegroundPaddingTop = 0;
60 
61     @ViewDebug.ExportedProperty(category = "padding")
62     private int mForegroundPaddingRight = 0;
63 
64     @ViewDebug.ExportedProperty(category = "padding")
65     private int mForegroundPaddingBottom = 0;
66 
67     private final Rect mSelfBounds = new Rect();
68     private final Rect mOverlayBounds = new Rect();
69 
70     @ViewDebug.ExportedProperty(category = "drawing")
71     private int mForegroundGravity = Gravity.FILL;
72 
73     /** {@hide} */
74     @ViewDebug.ExportedProperty(category = "drawing")
75     protected boolean mForegroundInPadding = true;
76 
77     boolean mForegroundBoundsChanged = false;
78 
FrameLayout(Context context)79     public FrameLayout(Context context) {
80         super(context);
81     }
82 
FrameLayout(Context context, AttributeSet attrs)83     public FrameLayout(Context context, AttributeSet attrs) {
84         this(context, attrs, 0);
85     }
86 
FrameLayout(Context context, AttributeSet attrs, int defStyle)87     public FrameLayout(Context context, AttributeSet attrs, int defStyle) {
88         super(context, attrs, defStyle);
89 
90         TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout,
91                     defStyle, 0);
92 
93         mForegroundGravity = a.getInt(
94                 com.android.internal.R.styleable.FrameLayout_foregroundGravity, mForegroundGravity);
95 
96         final Drawable d = a.getDrawable(com.android.internal.R.styleable.FrameLayout_foreground);
97         if (d != null) {
98             setForeground(d);
99         }
100 
101         if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) {
102             setMeasureAllChildren(true);
103         }
104 
105         mForegroundInPadding = a.getBoolean(
106                 com.android.internal.R.styleable.FrameLayout_foregroundInsidePadding, true);
107 
108         a.recycle();
109     }
110 
111     /**
112      * Describes how the foreground is positioned. Defaults to FILL.
113      *
114      * @param foregroundGravity See {@link android.view.Gravity}
115      *
116      * @attr ref android.R.styleable#FrameLayout_foregroundGravity
117      */
118     @android.view.RemotableViewMethod
setForegroundGravity(int foregroundGravity)119     public void setForegroundGravity(int foregroundGravity) {
120         if (mForegroundGravity != foregroundGravity) {
121             if ((foregroundGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
122                 foregroundGravity |= Gravity.LEFT;
123             }
124 
125             if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
126                 foregroundGravity |= Gravity.TOP;
127             }
128 
129             mForegroundGravity = foregroundGravity;
130 
131 
132             if (mForegroundGravity == Gravity.FILL && mForeground != null) {
133                 Rect padding = new Rect();
134                 if (mForeground.getPadding(padding)) {
135                     mForegroundPaddingLeft = padding.left;
136                     mForegroundPaddingTop = padding.top;
137                     mForegroundPaddingRight = padding.right;
138                     mForegroundPaddingBottom = padding.bottom;
139                 }
140             } else {
141                 mForegroundPaddingLeft = 0;
142                 mForegroundPaddingTop = 0;
143                 mForegroundPaddingRight = 0;
144                 mForegroundPaddingBottom = 0;
145             }
146 
147             requestLayout();
148         }
149     }
150 
151     /**
152      * {@inheritDoc}
153      */
154     @Override
verifyDrawable(Drawable who)155     protected boolean verifyDrawable(Drawable who) {
156         return super.verifyDrawable(who) || (who == mForeground);
157     }
158 
159     /**
160      * {@inheritDoc}
161      */
162     @Override
drawableStateChanged()163     protected void drawableStateChanged() {
164         super.drawableStateChanged();
165         if (mForeground != null && mForeground.isStateful()) {
166             mForeground.setState(getDrawableState());
167         }
168     }
169 
170     /**
171      * Returns a set of layout parameters with a width of
172      * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
173      * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}.
174      */
175     @Override
generateDefaultLayoutParams()176     protected LayoutParams generateDefaultLayoutParams() {
177         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
178     }
179 
180     /**
181      * Supply a Drawable that is to be rendered on top of all of the child
182      * views in the frame layout.  Any padding in the Drawable will be taken
183      * into account by ensuring that the children are inset to be placed
184      * inside of the padding area.
185      *
186      * @param drawable The Drawable to be drawn on top of the children.
187      *
188      * @attr ref android.R.styleable#FrameLayout_foreground
189      */
setForeground(Drawable drawable)190     public void setForeground(Drawable drawable) {
191         if (mForeground != drawable) {
192             if (mForeground != null) {
193                 mForeground.setCallback(null);
194                 unscheduleDrawable(mForeground);
195             }
196 
197             mForeground = drawable;
198             mForegroundPaddingLeft = 0;
199             mForegroundPaddingTop = 0;
200             mForegroundPaddingRight = 0;
201             mForegroundPaddingBottom = 0;
202 
203             if (drawable != null) {
204                 setWillNotDraw(false);
205                 drawable.setCallback(this);
206                 if (drawable.isStateful()) {
207                     drawable.setState(getDrawableState());
208                 }
209                 if (mForegroundGravity == Gravity.FILL) {
210                     Rect padding = new Rect();
211                     if (drawable.getPadding(padding)) {
212                         mForegroundPaddingLeft = padding.left;
213                         mForegroundPaddingTop = padding.top;
214                         mForegroundPaddingRight = padding.right;
215                         mForegroundPaddingBottom = padding.bottom;
216                     }
217                 }
218             }  else {
219                 setWillNotDraw(true);
220             }
221             requestLayout();
222             invalidate();
223         }
224     }
225 
226     /**
227      * Returns the drawable used as the foreground of this FrameLayout. The
228      * foreground drawable, if non-null, is always drawn on top of the children.
229      *
230      * @return A Drawable or null if no foreground was set.
231      */
getForeground()232     public Drawable getForeground() {
233         return mForeground;
234     }
235 
236     /**
237      * {@inheritDoc}
238      */
239     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)240     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
241         final int count = getChildCount();
242 
243         int maxHeight = 0;
244         int maxWidth = 0;
245 
246         // Find rightmost and bottommost child
247         for (int i = 0; i < count; i++) {
248             final View child = getChildAt(i);
249             if (mMeasureAllChildren || child.getVisibility() != GONE) {
250                 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
251                 maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
252                 maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
253             }
254         }
255 
256         // Account for padding too
257         maxWidth += mPaddingLeft + mPaddingRight + mForegroundPaddingLeft + mForegroundPaddingRight;
258         maxHeight += mPaddingTop + mPaddingBottom + mForegroundPaddingTop + mForegroundPaddingBottom;
259 
260         // Check against our minimum height and width
261         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
262         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
263 
264         // Check against our foreground's minimum height and width
265         final Drawable drawable = getForeground();
266         if (drawable != null) {
267             maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
268             maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
269         }
270 
271         setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
272                 resolveSize(maxHeight, heightMeasureSpec));
273     }
274 
275     /**
276      * {@inheritDoc}
277      */
278     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)279     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
280         final int count = getChildCount();
281 
282         final int parentLeft = mPaddingLeft + mForegroundPaddingLeft;
283         final int parentRight = right - left - mPaddingRight - mForegroundPaddingRight;
284 
285         final int parentTop = mPaddingTop + mForegroundPaddingTop;
286         final int parentBottom = bottom - top - mPaddingBottom - mForegroundPaddingBottom;
287 
288         mForegroundBoundsChanged = true;
289 
290         for (int i = 0; i < count; i++) {
291             final View child = getChildAt(i);
292             if (child.getVisibility() != GONE) {
293                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
294 
295                 final int width = child.getMeasuredWidth();
296                 final int height = child.getMeasuredHeight();
297 
298                 int childLeft = parentLeft;
299                 int childTop = parentTop;
300 
301                 final int gravity = lp.gravity;
302 
303                 if (gravity != -1) {
304                     final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
305                     final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
306 
307                     switch (horizontalGravity) {
308                         case Gravity.LEFT:
309                             childLeft = parentLeft + lp.leftMargin;
310                             break;
311                         case Gravity.CENTER_HORIZONTAL:
312                             childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
313                                     lp.leftMargin - lp.rightMargin;
314                             break;
315                         case Gravity.RIGHT:
316                             childLeft = parentRight - width - lp.rightMargin;
317                             break;
318                         default:
319                             childLeft = parentLeft + lp.leftMargin;
320                     }
321 
322                     switch (verticalGravity) {
323                         case Gravity.TOP:
324                             childTop = parentTop + lp.topMargin;
325                             break;
326                         case Gravity.CENTER_VERTICAL:
327                             childTop = parentTop + (parentBottom - parentTop - height) / 2 +
328                                     lp.topMargin - lp.bottomMargin;
329                             break;
330                         case Gravity.BOTTOM:
331                             childTop = parentBottom - height - lp.bottomMargin;
332                             break;
333                         default:
334                             childTop = parentTop + lp.topMargin;
335                     }
336                 }
337 
338                 child.layout(childLeft, childTop, childLeft + width, childTop + height);
339             }
340         }
341     }
342 
343     /**
344      * {@inheritDoc}
345      */
346     @Override
onSizeChanged(int w, int h, int oldw, int oldh)347     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
348         super.onSizeChanged(w, h, oldw, oldh);
349         mForegroundBoundsChanged = true;
350     }
351 
352     /**
353      * {@inheritDoc}
354      */
355     @Override
draw(Canvas canvas)356     public void draw(Canvas canvas) {
357         super.draw(canvas);
358 
359         if (mForeground != null) {
360             final Drawable foreground = mForeground;
361 
362             if (mForegroundBoundsChanged) {
363                 mForegroundBoundsChanged = false;
364                 final Rect selfBounds = mSelfBounds;
365                 final Rect overlayBounds = mOverlayBounds;
366 
367                 final int w = mRight-mLeft;
368                 final int h = mBottom-mTop;
369 
370                 if (mForegroundInPadding) {
371                     selfBounds.set(0, 0, w, h);
372                 } else {
373                     selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
374                 }
375 
376                 Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
377                         foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
378                 foreground.setBounds(overlayBounds);
379             }
380 
381             foreground.draw(canvas);
382         }
383     }
384 
385     /**
386      * {@inheritDoc}
387      */
388     @Override
gatherTransparentRegion(Region region)389     public boolean gatherTransparentRegion(Region region) {
390         boolean opaque = super.gatherTransparentRegion(region);
391         if (region != null && mForeground != null) {
392             applyDrawableToTransparentRegion(mForeground, region);
393         }
394         return opaque;
395     }
396 
397     /**
398      * Determines whether to measure all children or just those in
399      * the VISIBLE or INVISIBLE state when measuring. Defaults to false.
400      * @param measureAll true to consider children marked GONE, false otherwise.
401      * Default value is false.
402      *
403      * @attr ref android.R.styleable#FrameLayout_measureAllChildren
404      */
405     @android.view.RemotableViewMethod
setMeasureAllChildren(boolean measureAll)406     public void setMeasureAllChildren(boolean measureAll) {
407         mMeasureAllChildren = measureAll;
408     }
409 
410     /**
411      * Determines whether to measure all children or just those in
412      * the VISIBLE or INVISIBLE state when measuring.
413      */
getConsiderGoneChildrenWhenMeasuring()414     public boolean getConsiderGoneChildrenWhenMeasuring() {
415         return mMeasureAllChildren;
416     }
417 
418     /**
419      * {@inheritDoc}
420      */
421     @Override
generateLayoutParams(AttributeSet attrs)422     public LayoutParams generateLayoutParams(AttributeSet attrs) {
423         return new FrameLayout.LayoutParams(getContext(), attrs);
424     }
425 
426     /**
427      * {@inheritDoc}
428      */
429     @Override
checkLayoutParams(ViewGroup.LayoutParams p)430     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
431         return p instanceof LayoutParams;
432     }
433 
434     @Override
generateLayoutParams(ViewGroup.LayoutParams p)435     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
436         return new LayoutParams(p);
437     }
438 
439     /**
440      * Per-child layout information for layouts that support margins.
441      * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
442      * for a list of all child view attributes that this class supports.
443      */
444     public static class LayoutParams extends MarginLayoutParams {
445         /**
446          * The gravity to apply with the View to which these layout parameters
447          * are associated.
448          *
449          * @see android.view.Gravity
450          */
451         public int gravity = -1;
452 
453         /**
454          * {@inheritDoc}
455          */
LayoutParams(Context c, AttributeSet attrs)456         public LayoutParams(Context c, AttributeSet attrs) {
457             super(c, attrs);
458 
459             TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout);
460             gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1);
461             a.recycle();
462         }
463 
464         /**
465          * {@inheritDoc}
466          */
LayoutParams(int width, int height)467         public LayoutParams(int width, int height) {
468             super(width, height);
469         }
470 
471         /**
472          * Creates a new set of layout parameters with the specified width, height
473          * and weight.
474          *
475          * @param width the width, either {@link #MATCH_PARENT},
476          *        {@link #WRAP_CONTENT} or a fixed size in pixels
477          * @param height the height, either {@link #MATCH_PARENT},
478          *        {@link #WRAP_CONTENT} or a fixed size in pixels
479          * @param gravity the gravity
480          *
481          * @see android.view.Gravity
482          */
LayoutParams(int width, int height, int gravity)483         public LayoutParams(int width, int height, int gravity) {
484             super(width, height);
485             this.gravity = gravity;
486         }
487 
488         /**
489          * {@inheritDoc}
490          */
LayoutParams(ViewGroup.LayoutParams source)491         public LayoutParams(ViewGroup.LayoutParams source) {
492             super(source);
493         }
494 
495         /**
496          * {@inheritDoc}
497          */
LayoutParams(ViewGroup.MarginLayoutParams source)498         public LayoutParams(ViewGroup.MarginLayoutParams source) {
499             super(source);
500         }
501     }
502 }
503 
504