/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.Gravity; import android.widget.RemoteViews.RemoteView; /** * FrameLayout is designed to block out an area on the screen to display * a single item. You can add multiple children to a FrameLayout and control their * position within the FrameLayout using {@link android.widget.FrameLayout.LayoutParams#gravity}. * Children are drawn in a stack, with the most recently added child on top. * The size of the frame layout is the size of its largest child (plus padding), visible * or not (if the FrameLayout's parent permits). Views that are GONE are used for sizing * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()} * is set to true. * * @attr ref android.R.styleable#FrameLayout_foreground * @attr ref android.R.styleable#FrameLayout_foregroundGravity * @attr ref android.R.styleable#FrameLayout_measureAllChildren */ @RemoteView public class FrameLayout extends ViewGroup { @ViewDebug.ExportedProperty(category = "measurement") boolean mMeasureAllChildren = false; @ViewDebug.ExportedProperty(category = "drawing") private Drawable mForeground; @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingLeft = 0; @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingTop = 0; @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingRight = 0; @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingBottom = 0; private final Rect mSelfBounds = new Rect(); private final Rect mOverlayBounds = new Rect(); @ViewDebug.ExportedProperty(category = "drawing") private int mForegroundGravity = Gravity.FILL; /** {@hide} */ @ViewDebug.ExportedProperty(category = "drawing") protected boolean mForegroundInPadding = true; boolean mForegroundBoundsChanged = false; private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.LEFT; public FrameLayout(Context context) { super(context); } public FrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout, defStyle, 0); mForegroundGravity = a.getInt( com.android.internal.R.styleable.FrameLayout_foregroundGravity, mForegroundGravity); final Drawable d = a.getDrawable(com.android.internal.R.styleable.FrameLayout_foreground); if (d != null) { setForeground(d); } if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) { setMeasureAllChildren(true); } mForegroundInPadding = a.getBoolean( com.android.internal.R.styleable.FrameLayout_foregroundInsidePadding, true); a.recycle(); } /** * Describes how the foreground is positioned. Defaults to FILL. * * @param foregroundGravity See {@link android.view.Gravity} * * @attr ref android.R.styleable#FrameLayout_foregroundGravity */ @android.view.RemotableViewMethod public void setForegroundGravity(int foregroundGravity) { if (mForegroundGravity != foregroundGravity) { if ((foregroundGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { foregroundGravity |= Gravity.LEFT; } if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { foregroundGravity |= Gravity.TOP; } mForegroundGravity = foregroundGravity; if (mForegroundGravity == Gravity.FILL && mForeground != null) { Rect padding = new Rect(); if (mForeground.getPadding(padding)) { mForegroundPaddingLeft = padding.left; mForegroundPaddingTop = padding.top; mForegroundPaddingRight = padding.right; mForegroundPaddingBottom = padding.bottom; } } else { mForegroundPaddingLeft = 0; mForegroundPaddingTop = 0; mForegroundPaddingRight = 0; mForegroundPaddingBottom = 0; } requestLayout(); } } /** * {@inheritDoc} */ @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || (who == mForeground); } @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (mForeground != null) mForeground.jumpToCurrentState(); } /** * {@inheritDoc} */ @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mForeground != null && mForeground.isStateful()) { mForeground.setState(getDrawableState()); } } /** * Returns a set of layout parameters with a width of * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}. */ @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } /** * Supply a Drawable that is to be rendered on top of all of the child * views in the frame layout. Any padding in the Drawable will be taken * into account by ensuring that the children are inset to be placed * inside of the padding area. * * @param drawable The Drawable to be drawn on top of the children. * * @attr ref android.R.styleable#FrameLayout_foreground */ public void setForeground(Drawable drawable) { if (mForeground != drawable) { if (mForeground != null) { mForeground.setCallback(null); unscheduleDrawable(mForeground); } mForeground = drawable; mForegroundPaddingLeft = 0; mForegroundPaddingTop = 0; mForegroundPaddingRight = 0; mForegroundPaddingBottom = 0; if (drawable != null) { setWillNotDraw(false); drawable.setCallback(this); if (drawable.isStateful()) { drawable.setState(getDrawableState()); } if (mForegroundGravity == Gravity.FILL) { Rect padding = new Rect(); if (drawable.getPadding(padding)) { mForegroundPaddingLeft = padding.left; mForegroundPaddingTop = padding.top; mForegroundPaddingRight = padding.right; mForegroundPaddingBottom = padding.bottom; } } } else { setWillNotDraw(true); } requestLayout(); invalidate(); } } /** * Returns the drawable used as the foreground of this FrameLayout. The * foreground drawable, if non-null, is always drawn on top of the children. * * @return A Drawable or null if no foreground was set. */ public Drawable getForeground() { return mForeground; } /** * {@inheritDoc} */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int count = getChildCount(); int maxHeight = 0; int maxWidth = 0; int childState = 0; // Find rightmost and bottommost child for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); childState = combineMeasuredStates(childState, child.getMeasuredState()); } } // Account for padding too maxWidth += mPaddingLeft + mPaddingRight + mForegroundPaddingLeft + mForegroundPaddingRight; maxHeight += mPaddingTop + mPaddingBottom + mForegroundPaddingTop + mForegroundPaddingBottom; // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState<