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