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 17package android.widget; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Canvas; 22import android.graphics.Rect; 23import android.graphics.Region; 24import android.graphics.drawable.Drawable; 25import android.util.AttributeSet; 26import android.view.View; 27import android.view.ViewDebug; 28import android.view.ViewGroup; 29import android.view.Gravity; 30import 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 48public 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 74 public FrameLayout(Context context) { 75 super(context); 76 } 77 78 public FrameLayout(Context context, AttributeSet attrs) { 79 this(context, attrs, 0); 80 } 81 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 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 150 protected boolean verifyDrawable(Drawable who) { 151 return super.verifyDrawable(who) || (who == mForeground); 152 } 153 154 /** 155 * {@inheritDoc} 156 */ 157 @Override 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 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 */ 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 */ 227 public Drawable getForeground() { 228 return mForeground; 229 } 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override 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 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 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 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 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 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 */ 409 public boolean getConsiderGoneChildrenWhenMeasuring() { 410 return mMeasureAllChildren; 411 } 412 413 /** 414 * {@inheritDoc} 415 */ 416 @Override 417 public LayoutParams generateLayoutParams(AttributeSet attrs) { 418 return new FrameLayout.LayoutParams(getContext(), attrs); 419 } 420 421 /** 422 * {@inheritDoc} 423 */ 424 @Override 425 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 426 return p instanceof LayoutParams; 427 } 428 429 @Override 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 */ 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 */ 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 */ 478 public LayoutParams(int width, int height, int gravity) { 479 super(width, height); 480 this.gravity = gravity; 481 } 482 483 /** 484 * {@inheritDoc} 485 */ 486 public LayoutParams(ViewGroup.LayoutParams source) { 487 super(source); 488 } 489 490 /** 491 * {@inheritDoc} 492 */ 493 public LayoutParams(ViewGroup.MarginLayoutParams source) { 494 super(source); 495 } 496 } 497} 498 499