504 lines
17 KiB
Java
504 lines
17 KiB
Java
/*
|
|
* 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, but all
|
|
* children are pegged to the top left of the screen.
|
|
* 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;
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* {@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;
|
|
|
|
// 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());
|
|
}
|
|
}
|
|
|
|
// 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(resolveSize(maxWidth, widthMeasureSpec),
|
|
resolveSize(maxHeight, heightMeasureSpec));
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
final int count = getChildCount();
|
|
|
|
final int parentLeft = mPaddingLeft + mForegroundPaddingLeft;
|
|
final int parentRight = right - left - mPaddingRight - mForegroundPaddingRight;
|
|
|
|
final int parentTop = mPaddingTop + mForegroundPaddingTop;
|
|
final int parentBottom = bottom - top - mPaddingBottom - mForegroundPaddingBottom;
|
|
|
|
mForegroundBoundsChanged = true;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
final View child = getChildAt(i);
|
|
if (child.getVisibility() != GONE) {
|
|
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
|
|
final int width = child.getMeasuredWidth();
|
|
final int height = child.getMeasuredHeight();
|
|
|
|
int childLeft = parentLeft;
|
|
int childTop = parentTop;
|
|
|
|
final int gravity = lp.gravity;
|
|
|
|
if (gravity != -1) {
|
|
final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
|
|
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
|
|
|
|
switch (horizontalGravity) {
|
|
case Gravity.LEFT:
|
|
childLeft = parentLeft + lp.leftMargin;
|
|
break;
|
|
case Gravity.CENTER_HORIZONTAL:
|
|
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
|
|
lp.leftMargin - lp.rightMargin;
|
|
break;
|
|
case Gravity.RIGHT:
|
|
childLeft = parentRight - width - lp.rightMargin;
|
|
break;
|
|
default:
|
|
childLeft = parentLeft + lp.leftMargin;
|
|
}
|
|
|
|
switch (verticalGravity) {
|
|
case Gravity.TOP:
|
|
childTop = parentTop + lp.topMargin;
|
|
break;
|
|
case Gravity.CENTER_VERTICAL:
|
|
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
|
|
lp.topMargin - lp.bottomMargin;
|
|
break;
|
|
case Gravity.BOTTOM:
|
|
childTop = parentBottom - height - lp.bottomMargin;
|
|
break;
|
|
default:
|
|
childTop = parentTop + lp.topMargin;
|
|
}
|
|
}
|
|
|
|
child.layout(childLeft, childTop, childLeft + width, childTop + height);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
super.onSizeChanged(w, h, oldw, oldh);
|
|
mForegroundBoundsChanged = true;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void draw(Canvas canvas) {
|
|
super.draw(canvas);
|
|
|
|
if (mForeground != null) {
|
|
final Drawable foreground = mForeground;
|
|
|
|
if (mForegroundBoundsChanged) {
|
|
mForegroundBoundsChanged = false;
|
|
final Rect selfBounds = mSelfBounds;
|
|
final Rect overlayBounds = mOverlayBounds;
|
|
|
|
final int w = mRight-mLeft;
|
|
final int h = mBottom-mTop;
|
|
|
|
if (mForegroundInPadding) {
|
|
selfBounds.set(0, 0, w, h);
|
|
} else {
|
|
selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
|
|
}
|
|
|
|
Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
|
|
foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
|
|
foreground.setBounds(overlayBounds);
|
|
}
|
|
|
|
foreground.draw(canvas);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean gatherTransparentRegion(Region region) {
|
|
boolean opaque = super.gatherTransparentRegion(region);
|
|
if (region != null && mForeground != null) {
|
|
applyDrawableToTransparentRegion(mForeground, region);
|
|
}
|
|
return opaque;
|
|
}
|
|
|
|
/**
|
|
* Determines whether to measure all children or just those in
|
|
* the VISIBLE or INVISIBLE state when measuring. Defaults to false.
|
|
* @param measureAll true to consider children marked GONE, false otherwise.
|
|
* Default value is false.
|
|
*
|
|
* @attr ref android.R.styleable#FrameLayout_measureAllChildren
|
|
*/
|
|
@android.view.RemotableViewMethod
|
|
public void setMeasureAllChildren(boolean measureAll) {
|
|
mMeasureAllChildren = measureAll;
|
|
}
|
|
|
|
/**
|
|
* Determines whether to measure all children or just those in
|
|
* the VISIBLE or INVISIBLE state when measuring.
|
|
*/
|
|
public boolean getConsiderGoneChildrenWhenMeasuring() {
|
|
return mMeasureAllChildren;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
|
return new FrameLayout.LayoutParams(getContext(), attrs);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
|
return p instanceof LayoutParams;
|
|
}
|
|
|
|
@Override
|
|
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
|
|
return new LayoutParams(p);
|
|
}
|
|
|
|
/**
|
|
* Per-child layout information for layouts that support margins.
|
|
* See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
|
|
* for a list of all child view attributes that this class supports.
|
|
*/
|
|
public static class LayoutParams extends MarginLayoutParams {
|
|
/**
|
|
* The gravity to apply with the View to which these layout parameters
|
|
* are associated.
|
|
*
|
|
* @see android.view.Gravity
|
|
*/
|
|
public int gravity = -1;
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public LayoutParams(Context c, AttributeSet attrs) {
|
|
super(c, attrs);
|
|
|
|
TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout);
|
|
gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1);
|
|
a.recycle();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public LayoutParams(int width, int height) {
|
|
super(width, height);
|
|
}
|
|
|
|
/**
|
|
* Creates a new set of layout parameters with the specified width, height
|
|
* and weight.
|
|
*
|
|
* @param width the width, either {@link #MATCH_PARENT},
|
|
* {@link #WRAP_CONTENT} or a fixed size in pixels
|
|
* @param height the height, either {@link #MATCH_PARENT},
|
|
* {@link #WRAP_CONTENT} or a fixed size in pixels
|
|
* @param gravity the gravity
|
|
*
|
|
* @see android.view.Gravity
|
|
*/
|
|
public LayoutParams(int width, int height, int gravity) {
|
|
super(width, height);
|
|
this.gravity = gravity;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public LayoutParams(ViewGroup.LayoutParams source) {
|
|
super(source);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public LayoutParams(ViewGroup.MarginLayoutParams source) {
|
|
super(source);
|
|
}
|
|
}
|
|
}
|
|
|