2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.widget;
19 import com.android.internal.R;
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;
37 import java.util.ArrayList;
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
51 * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
54 * @attr ref android.R.styleable#FrameLayout_measureAllChildren
57 public class FrameLayout extends ViewGroup {
58 private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START;
60 @ViewDebug.ExportedProperty(category = "measurement")
61 boolean mMeasureAllChildren = false;
63 @ViewDebug.ExportedProperty(category = "padding")
64 private int mForegroundPaddingLeft = 0;
66 @ViewDebug.ExportedProperty(category = "padding")
67 private int mForegroundPaddingTop = 0;
69 @ViewDebug.ExportedProperty(category = "padding")
70 private int mForegroundPaddingRight = 0;
72 @ViewDebug.ExportedProperty(category = "padding")
73 private int mForegroundPaddingBottom = 0;
75 private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
77 public FrameLayout(@NonNull Context context) {
81 public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
82 this(context, attrs, 0);
85 public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
86 @AttrRes int defStyleAttr) {
87 this(context, attrs, defStyleAttr, 0);
90 public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
91 @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
92 super(context, attrs, defStyleAttr, defStyleRes);
94 final TypedArray a = context.obtainStyledAttributes(
95 attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes);
97 if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) {
98 setMeasureAllChildren(true);
105 * Describes how the foreground is positioned. Defaults to START and TOP.
107 * @param foregroundGravity See {@link android.view.Gravity}
109 * @see #getForegroundGravity()
111 * @attr ref android.R.styleable#View_foregroundGravity
113 @android.view.RemotableViewMethod
114 public void setForegroundGravity(int foregroundGravity) {
115 if (getForegroundGravity() != foregroundGravity) {
116 super.setForegroundGravity(foregroundGravity);
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;
129 mForegroundPaddingLeft = 0;
130 mForegroundPaddingTop = 0;
131 mForegroundPaddingRight = 0;
132 mForegroundPaddingBottom = 0;
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}.
145 protected LayoutParams generateDefaultLayoutParams() {
146 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
149 int getPaddingLeftWithForeground() {
150 return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
151 mPaddingLeft + mForegroundPaddingLeft;
154 int getPaddingRightWithForeground() {
155 return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
156 mPaddingRight + mForegroundPaddingRight;
159 private int getPaddingTopWithForeground() {
160 return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
161 mPaddingTop + mForegroundPaddingTop;
164 private int getPaddingBottomWithForeground() {
165 return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
166 mPaddingBottom + mForegroundPaddingBottom;
170 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
171 int count = getChildCount();
173 final boolean measureMatchParentChildren =
174 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
175 MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
176 mMatchParentChildren.clear();
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);
201 // Account for padding too
202 maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
203 maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
205 // Check against our minimum height and width
206 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
207 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
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());
216 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
217 resolveSizeAndState(maxHeight, heightMeasureSpec,
218 childState << MEASURED_HEIGHT_STATE_SHIFT));
220 count = mMatchParentChildren.size();
222 for (int i = 0; i < count; i++) {
223 final View child = mMatchParentChildren.get(i);
224 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
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);
234 childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
235 getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
236 lp.leftMargin + lp.rightMargin,
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);
248 childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
249 getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
250 lp.topMargin + lp.bottomMargin,
254 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
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 */);
264 void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
265 final int count = getChildCount();
267 final int parentLeft = getPaddingLeftWithForeground();
268 final int parentRight = right - left - getPaddingRightWithForeground();
270 final int parentTop = getPaddingTopWithForeground();
271 final int parentBottom = bottom - top - getPaddingBottomWithForeground();
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();
278 final int width = child.getMeasuredWidth();
279 final int height = child.getMeasuredHeight();
284 int gravity = lp.gravity;
286 gravity = DEFAULT_CHILD_GRAVITY;
289 final int layoutDirection = getLayoutDirection();
290 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
291 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
293 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
294 case Gravity.CENTER_HORIZONTAL:
295 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
296 lp.leftMargin - lp.rightMargin;
299 if (!forceLeftGravity) {
300 childLeft = parentRight - width - lp.rightMargin;
305 childLeft = parentLeft + lp.leftMargin;
308 switch (verticalGravity) {
310 childTop = parentTop + lp.topMargin;
312 case Gravity.CENTER_VERTICAL:
313 childTop = parentTop + (parentBottom - parentTop - height) / 2 +
314 lp.topMargin - lp.bottomMargin;
317 childTop = parentBottom - height - lp.bottomMargin;
320 childTop = parentTop + lp.topMargin;
323 child.layout(childLeft, childTop, childLeft + width, childTop + height);
329 * Sets whether to consider all children, or just those in
330 * the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
332 * @param measureAll true to consider children marked GONE, false otherwise.
333 * Default value is false.
335 * @attr ref android.R.styleable#FrameLayout_measureAllChildren
337 @android.view.RemotableViewMethod
338 public void setMeasureAllChildren(boolean measureAll) {
339 mMeasureAllChildren = measureAll;
343 * Determines whether all children, or just those in the VISIBLE or
344 * INVISIBLE state, are considered when measuring.
346 * @return Whether all children are considered when measuring.
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()}.
354 public boolean getConsiderGoneChildrenWhenMeasuring() {
355 return getMeasureAllChildren();
359 * Determines whether all children, or just those in the VISIBLE or
360 * INVISIBLE state, are considered when measuring.
362 * @return Whether all children are considered when measuring.
364 public boolean getMeasureAllChildren() {
365 return mMeasureAllChildren;
369 public LayoutParams generateLayoutParams(AttributeSet attrs) {
370 return new FrameLayout.LayoutParams(getContext(), attrs);
374 public boolean shouldDelayChildPressedState() {
379 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
380 return p instanceof LayoutParams;
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);
390 return new LayoutParams(lp);
395 public CharSequence getAccessibilityClassName() {
396 return FrameLayout.class.getName();
401 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
402 super.encodeProperties(encoder);
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);
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.
416 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
418 public static class LayoutParams extends MarginLayoutParams {
420 * Value for {@link #gravity} indicating that a gravity has not been
421 * explicitly specified.
423 public static final int UNSPECIFIED_GRAVITY = -1;
426 * The gravity to apply with the View to which these layout parameters
429 * The default value is {@link #UNSPECIFIED_GRAVITY}, which is treated
430 * by FrameLayout as {@code Gravity.TOP | Gravity.START}.
432 * @see android.view.Gravity
433 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
435 public int gravity = UNSPECIFIED_GRAVITY;
437 public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
440 final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout);
441 gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity, UNSPECIFIED_GRAVITY);
445 public LayoutParams(int width, int height) {
446 super(width, height);
450 * Creates a new set of layout parameters with the specified width, height
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
459 * @see android.view.Gravity
461 public LayoutParams(int width, int height, int gravity) {
462 super(width, height);
463 this.gravity = gravity;
466 public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
470 public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
475 * Copy constructor. Clones the width, height, margin values, and
476 * gravity of the source.
478 * @param source The layout params to copy from.
480 public LayoutParams(@NonNull LayoutParams source) {
483 this.gravity = source.gravity;