• 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 com.android.internal.R;
20 
21 import android.annotation.AttrRes;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.StyleRes;
25 import android.content.Context;
26 import android.content.res.TypedArray;
27 import android.graphics.Rect;
28 import android.graphics.drawable.Drawable;
29 import android.util.AttributeSet;
30 import android.view.Gravity;
31 import android.view.View;
32 import android.view.ViewDebug;
33 import android.view.ViewGroup;
34 import android.view.ViewHierarchyEncoder;
35 import android.widget.RemoteViews.RemoteView;
36 
37 import java.util.ArrayList;
38 
39 /**
40  * FrameLayout is designed to block out an area on the screen to display
41  * a single item. Generally, FrameLayout should be used to hold a single child view, because it can
42  * be difficult to organize child views in a way that's scalable to different screen sizes without
43  * the children overlapping each other. You can, however, add multiple children to a FrameLayout
44  * and control their position within the FrameLayout by assigning gravity to each child, using the
45  * <a href="FrameLayout.LayoutParams.html#attr_android:layout_gravity">{@code
46  * android:layout_gravity}</a> attribute.
47  * <p>Child views are drawn in a stack, with the most recently added child on top.
48  * The size of the FrameLayout is the size of its largest child (plus padding), visible
49  * or not (if the FrameLayout's parent permits). Views that are {@link android.view.View#GONE} are
50  * used for sizing
51  * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
52  * is set to true.
53  *
54  * @attr ref android.R.styleable#FrameLayout_measureAllChildren
55  */
56 @RemoteView
57 public class FrameLayout extends ViewGroup {
58     private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START;
59 
60     @ViewDebug.ExportedProperty(category = "measurement")
61     boolean mMeasureAllChildren = false;
62 
63     @ViewDebug.ExportedProperty(category = "padding")
64     private int mForegroundPaddingLeft = 0;
65 
66     @ViewDebug.ExportedProperty(category = "padding")
67     private int mForegroundPaddingTop = 0;
68 
69     @ViewDebug.ExportedProperty(category = "padding")
70     private int mForegroundPaddingRight = 0;
71 
72     @ViewDebug.ExportedProperty(category = "padding")
73     private int mForegroundPaddingBottom = 0;
74 
75     private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
76 
FrameLayout(@onNull Context context)77     public FrameLayout(@NonNull Context context) {
78         super(context);
79     }
80 
FrameLayout(@onNull Context context, @Nullable AttributeSet attrs)81     public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
82         this(context, attrs, 0);
83     }
84 
FrameLayout(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr)85     public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
86             @AttrRes int defStyleAttr) {
87         this(context, attrs, defStyleAttr, 0);
88     }
89 
FrameLayout(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)90     public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
91             @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
92         super(context, attrs, defStyleAttr, defStyleRes);
93 
94         final TypedArray a = context.obtainStyledAttributes(
95                 attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes);
96 
97         if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) {
98             setMeasureAllChildren(true);
99         }
100 
101         a.recycle();
102     }
103 
104     /**
105      * Describes how the foreground is positioned. Defaults to START and TOP.
106      *
107      * @param foregroundGravity See {@link android.view.Gravity}
108      *
109      * @see #getForegroundGravity()
110      *
111      * @attr ref android.R.styleable#View_foregroundGravity
112      */
113     @android.view.RemotableViewMethod
setForegroundGravity(int foregroundGravity)114     public void setForegroundGravity(int foregroundGravity) {
115         if (getForegroundGravity() != foregroundGravity) {
116             super.setForegroundGravity(foregroundGravity);
117 
118             // calling get* again here because the set above may apply default constraints
119             final Drawable foreground = getForeground();
120             if (getForegroundGravity() == Gravity.FILL && foreground != null) {
121                 Rect padding = new Rect();
122                 if (foreground.getPadding(padding)) {
123                     mForegroundPaddingLeft = padding.left;
124                     mForegroundPaddingTop = padding.top;
125                     mForegroundPaddingRight = padding.right;
126                     mForegroundPaddingBottom = padding.bottom;
127                 }
128             } else {
129                 mForegroundPaddingLeft = 0;
130                 mForegroundPaddingTop = 0;
131                 mForegroundPaddingRight = 0;
132                 mForegroundPaddingBottom = 0;
133             }
134 
135             requestLayout();
136         }
137     }
138 
139     /**
140      * Returns a set of layout parameters with a width of
141      * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
142      * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}.
143      */
144     @Override
generateDefaultLayoutParams()145     protected LayoutParams generateDefaultLayoutParams() {
146         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
147     }
148 
getPaddingLeftWithForeground()149     int getPaddingLeftWithForeground() {
150         return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
151             mPaddingLeft + mForegroundPaddingLeft;
152     }
153 
getPaddingRightWithForeground()154     int getPaddingRightWithForeground() {
155         return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
156             mPaddingRight + mForegroundPaddingRight;
157     }
158 
getPaddingTopWithForeground()159     private int getPaddingTopWithForeground() {
160         return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
161             mPaddingTop + mForegroundPaddingTop;
162     }
163 
getPaddingBottomWithForeground()164     private int getPaddingBottomWithForeground() {
165         return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
166             mPaddingBottom + mForegroundPaddingBottom;
167     }
168 
169     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)170     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
171         int count = getChildCount();
172 
173         final boolean measureMatchParentChildren =
174                 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
175                 MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
176         mMatchParentChildren.clear();
177 
178         int maxHeight = 0;
179         int maxWidth = 0;
180         int childState = 0;
181 
182         for (int i = 0; i < count; i++) {
183             final View child = getChildAt(i);
184             if (mMeasureAllChildren || child.getVisibility() != GONE) {
185                 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
186                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
187                 maxWidth = Math.max(maxWidth,
188                         child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
189                 maxHeight = Math.max(maxHeight,
190                         child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
191                 childState = combineMeasuredStates(childState, child.getMeasuredState());
192                 if (measureMatchParentChildren) {
193                     if (lp.width == LayoutParams.MATCH_PARENT ||
194                             lp.height == LayoutParams.MATCH_PARENT) {
195                         mMatchParentChildren.add(child);
196                     }
197                 }
198             }
199         }
200 
201         // Account for padding too
202         maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
203         maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
204 
205         // Check against our minimum height and width
206         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
207         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
208 
209         // Check against our foreground's minimum height and width
210         final Drawable drawable = getForeground();
211         if (drawable != null) {
212             maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
213             maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
214         }
215 
216         setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
217                 resolveSizeAndState(maxHeight, heightMeasureSpec,
218                         childState << MEASURED_HEIGHT_STATE_SHIFT));
219 
220         count = mMatchParentChildren.size();
221         if (count > 1) {
222             for (int i = 0; i < count; i++) {
223                 final View child = mMatchParentChildren.get(i);
224                 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
225 
226                 final int childWidthMeasureSpec;
227                 if (lp.width == LayoutParams.MATCH_PARENT) {
228                     final int width = Math.max(0, getMeasuredWidth()
229                             - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
230                             - lp.leftMargin - lp.rightMargin);
231                     childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
232                             width, MeasureSpec.EXACTLY);
233                 } else {
234                     childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
235                             getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
236                             lp.leftMargin + lp.rightMargin,
237                             lp.width);
238                 }
239 
240                 final int childHeightMeasureSpec;
241                 if (lp.height == LayoutParams.MATCH_PARENT) {
242                     final int height = Math.max(0, getMeasuredHeight()
243                             - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
244                             - lp.topMargin - lp.bottomMargin);
245                     childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
246                             height, MeasureSpec.EXACTLY);
247                 } else {
248                     childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
249                             getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
250                             lp.topMargin + lp.bottomMargin,
251                             lp.height);
252                 }
253 
254                 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
255             }
256         }
257     }
258 
259     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)260     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
261         layoutChildren(left, top, right, bottom, false /* no force left gravity */);
262     }
263 
layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity)264     void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
265         final int count = getChildCount();
266 
267         final int parentLeft = getPaddingLeftWithForeground();
268         final int parentRight = right - left - getPaddingRightWithForeground();
269 
270         final int parentTop = getPaddingTopWithForeground();
271         final int parentBottom = bottom - top - getPaddingBottomWithForeground();
272 
273         for (int i = 0; i < count; i++) {
274             final View child = getChildAt(i);
275             if (child.getVisibility() != GONE) {
276                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
277 
278                 final int width = child.getMeasuredWidth();
279                 final int height = child.getMeasuredHeight();
280 
281                 int childLeft;
282                 int childTop;
283 
284                 int gravity = lp.gravity;
285                 if (gravity == -1) {
286                     gravity = DEFAULT_CHILD_GRAVITY;
287                 }
288 
289                 final int layoutDirection = getLayoutDirection();
290                 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
291                 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
292 
293                 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
294                     case Gravity.CENTER_HORIZONTAL:
295                         childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
296                         lp.leftMargin - lp.rightMargin;
297                         break;
298                     case Gravity.RIGHT:
299                         if (!forceLeftGravity) {
300                             childLeft = parentRight - width - lp.rightMargin;
301                             break;
302                         }
303                     case Gravity.LEFT:
304                     default:
305                         childLeft = parentLeft + lp.leftMargin;
306                 }
307 
308                 switch (verticalGravity) {
309                     case Gravity.TOP:
310                         childTop = parentTop + lp.topMargin;
311                         break;
312                     case Gravity.CENTER_VERTICAL:
313                         childTop = parentTop + (parentBottom - parentTop - height) / 2 +
314                         lp.topMargin - lp.bottomMargin;
315                         break;
316                     case Gravity.BOTTOM:
317                         childTop = parentBottom - height - lp.bottomMargin;
318                         break;
319                     default:
320                         childTop = parentTop + lp.topMargin;
321                 }
322 
323                 child.layout(childLeft, childTop, childLeft + width, childTop + height);
324             }
325         }
326     }
327 
328     /**
329      * Sets whether to consider all children, or just those in
330      * the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
331      *
332      * @param measureAll true to consider children marked GONE, false otherwise.
333      * Default value is false.
334      *
335      * @attr ref android.R.styleable#FrameLayout_measureAllChildren
336      */
337     @android.view.RemotableViewMethod
setMeasureAllChildren(boolean measureAll)338     public void setMeasureAllChildren(boolean measureAll) {
339         mMeasureAllChildren = measureAll;
340     }
341 
342     /**
343      * Determines whether all children, or just those in the VISIBLE or
344      * INVISIBLE state, are considered when measuring.
345      *
346      * @return Whether all children are considered when measuring.
347      *
348      * @deprecated This method is deprecated in favor of
349      * {@link #getMeasureAllChildren() getMeasureAllChildren()}, which was
350      * renamed for consistency with
351      * {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}.
352      */
353     @Deprecated
getConsiderGoneChildrenWhenMeasuring()354     public boolean getConsiderGoneChildrenWhenMeasuring() {
355         return getMeasureAllChildren();
356     }
357 
358     /**
359      * Determines whether all children, or just those in the VISIBLE or
360      * INVISIBLE state, are considered when measuring.
361      *
362      * @return Whether all children are considered when measuring.
363      */
getMeasureAllChildren()364     public boolean getMeasureAllChildren() {
365         return mMeasureAllChildren;
366     }
367 
368     @Override
generateLayoutParams(AttributeSet attrs)369     public LayoutParams generateLayoutParams(AttributeSet attrs) {
370         return new FrameLayout.LayoutParams(getContext(), attrs);
371     }
372 
373     @Override
shouldDelayChildPressedState()374     public boolean shouldDelayChildPressedState() {
375         return false;
376     }
377 
378     @Override
checkLayoutParams(ViewGroup.LayoutParams p)379     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
380         return p instanceof LayoutParams;
381     }
382 
383     @Override
generateLayoutParams(ViewGroup.LayoutParams lp)384     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
385         if (lp instanceof LayoutParams) {
386             return new LayoutParams((LayoutParams) lp);
387         } else if (lp instanceof MarginLayoutParams) {
388             return new LayoutParams((MarginLayoutParams) lp);
389         } else {
390             return new LayoutParams(lp);
391         }
392     }
393 
394     @Override
getAccessibilityClassName()395     public CharSequence getAccessibilityClassName() {
396         return FrameLayout.class.getName();
397     }
398 
399     /** @hide */
400     @Override
encodeProperties(@onNull ViewHierarchyEncoder encoder)401     protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
402         super.encodeProperties(encoder);
403 
404         encoder.addProperty("measurement:measureAllChildren", mMeasureAllChildren);
405         encoder.addProperty("padding:foregroundPaddingLeft", mForegroundPaddingLeft);
406         encoder.addProperty("padding:foregroundPaddingTop", mForegroundPaddingTop);
407         encoder.addProperty("padding:foregroundPaddingRight", mForegroundPaddingRight);
408         encoder.addProperty("padding:foregroundPaddingBottom", mForegroundPaddingBottom);
409     }
410 
411     /**
412      * Per-child layout information for layouts that support margins.
413      * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
414      * for a list of all child view attributes that this class supports.
415      *
416      * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
417      */
418     public static class LayoutParams extends MarginLayoutParams {
419         /**
420          * Value for {@link #gravity} indicating that a gravity has not been
421          * explicitly specified.
422          */
423         public static final int UNSPECIFIED_GRAVITY = -1;
424 
425         /**
426          * The gravity to apply with the View to which these layout parameters
427          * are associated.
428          * <p>
429          * The default value is {@link #UNSPECIFIED_GRAVITY}, which is treated
430          * by FrameLayout as {@code Gravity.TOP | Gravity.START}.
431          *
432          * @see android.view.Gravity
433          * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
434          */
435         public int gravity = UNSPECIFIED_GRAVITY;
436 
LayoutParams(@onNull Context c, @Nullable AttributeSet attrs)437         public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
438             super(c, attrs);
439 
440             final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout);
441             gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity, UNSPECIFIED_GRAVITY);
442             a.recycle();
443         }
444 
LayoutParams(int width, int height)445         public LayoutParams(int width, int height) {
446             super(width, height);
447         }
448 
449         /**
450          * Creates a new set of layout parameters with the specified width, height
451          * and weight.
452          *
453          * @param width the width, either {@link #MATCH_PARENT},
454          *              {@link #WRAP_CONTENT} or a fixed size in pixels
455          * @param height the height, either {@link #MATCH_PARENT},
456          *               {@link #WRAP_CONTENT} or a fixed size in pixels
457          * @param gravity the gravity
458          *
459          * @see android.view.Gravity
460          */
LayoutParams(int width, int height, int gravity)461         public LayoutParams(int width, int height, int gravity) {
462             super(width, height);
463             this.gravity = gravity;
464         }
465 
LayoutParams(@onNull ViewGroup.LayoutParams source)466         public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
467             super(source);
468         }
469 
LayoutParams(@onNull ViewGroup.MarginLayoutParams source)470         public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
471             super(source);
472         }
473 
474         /**
475          * Copy constructor. Clones the width, height, margin values, and
476          * gravity of the source.
477          *
478          * @param source The layout params to copy from.
479          */
LayoutParams(@onNull LayoutParams source)480         public LayoutParams(@NonNull LayoutParams source) {
481             super(source);
482 
483             this.gravity = source.gravity;
484         }
485     }
486 }
487