FrameLayout.java revision b25825a1b159a469f80423800daa7f5b6250acce
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 32import java.util.ArrayList; 33 34 35/** 36 * FrameLayout is designed to block out an area on the screen to display 37 * a single item. You can add multiple children to a FrameLayout and control their 38 * position within the FrameLayout using {@link android.widget.FrameLayout.LayoutParams#gravity}. 39 * Children are drawn in a stack, with the most recently added child on top. 40 * The size of the frame layout is the size of its largest child (plus padding), visible 41 * or not (if the FrameLayout's parent permits). Views that are GONE are used for sizing 42 * only if {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()} 43 * is set to true. 44 * 45 * @attr ref android.R.styleable#FrameLayout_foreground 46 * @attr ref android.R.styleable#FrameLayout_foregroundGravity 47 * @attr ref android.R.styleable#FrameLayout_measureAllChildren 48 */ 49@RemoteView 50public class FrameLayout extends ViewGroup { 51 private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.LEFT; 52 53 @ViewDebug.ExportedProperty(category = "measurement") 54 boolean mMeasureAllChildren = false; 55 56 @ViewDebug.ExportedProperty(category = "drawing") 57 private Drawable mForeground; 58 59 @ViewDebug.ExportedProperty(category = "padding") 60 private int mForegroundPaddingLeft = 0; 61 62 @ViewDebug.ExportedProperty(category = "padding") 63 private int mForegroundPaddingTop = 0; 64 65 @ViewDebug.ExportedProperty(category = "padding") 66 private int mForegroundPaddingRight = 0; 67 68 @ViewDebug.ExportedProperty(category = "padding") 69 private int mForegroundPaddingBottom = 0; 70 71 private final Rect mSelfBounds = new Rect(); 72 private final Rect mOverlayBounds = new Rect(); 73 74 @ViewDebug.ExportedProperty(category = "drawing") 75 private int mForegroundGravity = Gravity.FILL; 76 77 /** {@hide} */ 78 @ViewDebug.ExportedProperty(category = "drawing") 79 protected boolean mForegroundInPadding = true; 80 81 boolean mForegroundBoundsChanged = false; 82 83 private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1); 84 85 public FrameLayout(Context context) { 86 super(context); 87 } 88 89 public FrameLayout(Context context, AttributeSet attrs) { 90 this(context, attrs, 0); 91 } 92 93 public FrameLayout(Context context, AttributeSet attrs, int defStyle) { 94 super(context, attrs, defStyle); 95 96 TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout, 97 defStyle, 0); 98 99 mForegroundGravity = a.getInt( 100 com.android.internal.R.styleable.FrameLayout_foregroundGravity, mForegroundGravity); 101 102 final Drawable d = a.getDrawable(com.android.internal.R.styleable.FrameLayout_foreground); 103 if (d != null) { 104 setForeground(d); 105 } 106 107 if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) { 108 setMeasureAllChildren(true); 109 } 110 111 mForegroundInPadding = a.getBoolean( 112 com.android.internal.R.styleable.FrameLayout_foregroundInsidePadding, true); 113 114 a.recycle(); 115 } 116 117 /** 118 * Describes how the foreground is positioned. Defaults to FILL. 119 * 120 * @param foregroundGravity See {@link android.view.Gravity} 121 * 122 * @attr ref android.R.styleable#FrameLayout_foregroundGravity 123 */ 124 @android.view.RemotableViewMethod 125 public void setForegroundGravity(int foregroundGravity) { 126 if (mForegroundGravity != foregroundGravity) { 127 if ((foregroundGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { 128 foregroundGravity |= Gravity.LEFT; 129 } 130 131 if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { 132 foregroundGravity |= Gravity.TOP; 133 } 134 135 mForegroundGravity = foregroundGravity; 136 137 138 if (mForegroundGravity == Gravity.FILL && mForeground != null) { 139 Rect padding = new Rect(); 140 if (mForeground.getPadding(padding)) { 141 mForegroundPaddingLeft = padding.left; 142 mForegroundPaddingTop = padding.top; 143 mForegroundPaddingRight = padding.right; 144 mForegroundPaddingBottom = padding.bottom; 145 } 146 } else { 147 mForegroundPaddingLeft = 0; 148 mForegroundPaddingTop = 0; 149 mForegroundPaddingRight = 0; 150 mForegroundPaddingBottom = 0; 151 } 152 153 requestLayout(); 154 } 155 } 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override 161 protected boolean verifyDrawable(Drawable who) { 162 return super.verifyDrawable(who) || (who == mForeground); 163 } 164 165 @Override 166 public void jumpDrawablesToCurrentState() { 167 super.jumpDrawablesToCurrentState(); 168 if (mForeground != null) mForeground.jumpToCurrentState(); 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override 175 protected void drawableStateChanged() { 176 super.drawableStateChanged(); 177 if (mForeground != null && mForeground.isStateful()) { 178 mForeground.setState(getDrawableState()); 179 } 180 } 181 182 /** 183 * Returns a set of layout parameters with a width of 184 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, 185 * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}. 186 */ 187 @Override 188 protected LayoutParams generateDefaultLayoutParams() { 189 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 190 } 191 192 /** 193 * Supply a Drawable that is to be rendered on top of all of the child 194 * views in the frame layout. Any padding in the Drawable will be taken 195 * into account by ensuring that the children are inset to be placed 196 * inside of the padding area. 197 * 198 * @param drawable The Drawable to be drawn on top of the children. 199 * 200 * @attr ref android.R.styleable#FrameLayout_foreground 201 */ 202 public void setForeground(Drawable drawable) { 203 if (mForeground != drawable) { 204 if (mForeground != null) { 205 mForeground.setCallback(null); 206 unscheduleDrawable(mForeground); 207 } 208 209 mForeground = drawable; 210 mForegroundPaddingLeft = 0; 211 mForegroundPaddingTop = 0; 212 mForegroundPaddingRight = 0; 213 mForegroundPaddingBottom = 0; 214 215 if (drawable != null) { 216 setWillNotDraw(false); 217 drawable.setCallback(this); 218 if (drawable.isStateful()) { 219 drawable.setState(getDrawableState()); 220 } 221 if (mForegroundGravity == Gravity.FILL) { 222 Rect padding = new Rect(); 223 if (drawable.getPadding(padding)) { 224 mForegroundPaddingLeft = padding.left; 225 mForegroundPaddingTop = padding.top; 226 mForegroundPaddingRight = padding.right; 227 mForegroundPaddingBottom = padding.bottom; 228 } 229 } 230 } else { 231 setWillNotDraw(true); 232 } 233 requestLayout(); 234 invalidate(); 235 } 236 } 237 238 /** 239 * Returns the drawable used as the foreground of this FrameLayout. The 240 * foreground drawable, if non-null, is always drawn on top of the children. 241 * 242 * @return A Drawable or null if no foreground was set. 243 */ 244 public Drawable getForeground() { 245 return mForeground; 246 } 247 248 /** 249 * {@inheritDoc} 250 */ 251 @Override 252 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 253 int count = getChildCount(); 254 255 final boolean measureMatchParentChildren = 256 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || 257 MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; 258 mMatchParentChildren.clear(); 259 260 int maxHeight = 0; 261 int maxWidth = 0; 262 int childState = 0; 263 264 for (int i = 0; i < count; i++) { 265 final View child = getChildAt(i); 266 if (mMeasureAllChildren || child.getVisibility() != GONE) { 267 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); 268 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 269 maxWidth = Math.max(maxWidth, 270 child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 271 maxHeight = Math.max(maxHeight, 272 child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 273 childState = combineMeasuredStates(childState, child.getMeasuredState()); 274 if (measureMatchParentChildren) { 275 if (lp.width == LayoutParams.MATCH_PARENT || 276 lp.height == LayoutParams.MATCH_PARENT) { 277 mMatchParentChildren.add(child); 278 } 279 } 280 } 281 } 282 283 // Account for padding too 284 maxWidth += mPaddingLeft + mPaddingRight + mForegroundPaddingLeft + mForegroundPaddingRight; 285 maxHeight += mPaddingTop + mPaddingBottom + mForegroundPaddingTop + mForegroundPaddingBottom; 286 287 // Check against our minimum height and width 288 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 289 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 290 291 // Check against our foreground's minimum height and width 292 final Drawable drawable = getForeground(); 293 if (drawable != null) { 294 maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); 295 maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); 296 } 297 298 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 299 resolveSizeAndState(maxHeight, heightMeasureSpec, 300 childState << MEASURED_HEIGHT_STATE_SHIFT)); 301 302 count = mMatchParentChildren.size(); 303 if (count > 1) { 304 for (int i = 0; i < count; i++) { 305 final View child = mMatchParentChildren.get(i); 306 307 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 308 int childWidthMeasureSpec; 309 int childHeightMeasureSpec; 310 311 if (lp.width == LayoutParams.MATCH_PARENT) { 312 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - 313 mPaddingLeft - mPaddingRight - lp.leftMargin - lp.rightMargin, 314 MeasureSpec.EXACTLY); 315 } else { 316 childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 317 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin, 318 lp.width); 319 } 320 321 if (lp.height == LayoutParams.MATCH_PARENT) { 322 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - 323 mPaddingTop - mPaddingBottom - lp.topMargin - lp.bottomMargin, 324 MeasureSpec.EXACTLY); 325 } else { 326 childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 327 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin, 328 lp.height); 329 } 330 331 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 332 } 333 } 334 } 335 336 /** 337 * {@inheritDoc} 338 */ 339 @Override 340 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 341 final int count = getChildCount(); 342 343 final int parentLeft = mPaddingLeft + mForegroundPaddingLeft; 344 final int parentRight = right - left - mPaddingRight - mForegroundPaddingRight; 345 346 final int parentTop = mPaddingTop + mForegroundPaddingTop; 347 final int parentBottom = bottom - top - mPaddingBottom - mForegroundPaddingBottom; 348 349 mForegroundBoundsChanged = true; 350 351 for (int i = 0; i < count; i++) { 352 final View child = getChildAt(i); 353 if (child.getVisibility() != GONE) { 354 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 355 356 final int width = child.getMeasuredWidth(); 357 final int height = child.getMeasuredHeight(); 358 359 int childLeft; 360 int childTop; 361 362 int gravity = lp.gravity; 363 if (gravity == -1) { 364 gravity = DEFAULT_CHILD_GRAVITY; 365 } 366 367 final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; 368 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; 369 370 switch (horizontalGravity) { 371 case Gravity.LEFT: 372 childLeft = parentLeft + lp.leftMargin; 373 break; 374 case Gravity.CENTER_HORIZONTAL: 375 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + 376 lp.leftMargin - lp.rightMargin; 377 break; 378 case Gravity.RIGHT: 379 childLeft = parentRight - width - lp.rightMargin; 380 break; 381 default: 382 childLeft = parentLeft + lp.leftMargin; 383 } 384 385 switch (verticalGravity) { 386 case Gravity.TOP: 387 childTop = parentTop + lp.topMargin; 388 break; 389 case Gravity.CENTER_VERTICAL: 390 childTop = parentTop + (parentBottom - parentTop - height) / 2 + 391 lp.topMargin - lp.bottomMargin; 392 break; 393 case Gravity.BOTTOM: 394 childTop = parentBottom - height - lp.bottomMargin; 395 break; 396 default: 397 childTop = parentTop + lp.topMargin; 398 } 399 400 child.layout(childLeft, childTop, childLeft + width, childTop + height); 401 } 402 } 403 } 404 405 /** 406 * {@inheritDoc} 407 */ 408 @Override 409 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 410 super.onSizeChanged(w, h, oldw, oldh); 411 mForegroundBoundsChanged = true; 412 } 413 414 /** 415 * {@inheritDoc} 416 */ 417 @Override 418 public void draw(Canvas canvas) { 419 super.draw(canvas); 420 421 if (mForeground != null) { 422 final Drawable foreground = mForeground; 423 424 if (mForegroundBoundsChanged) { 425 mForegroundBoundsChanged = false; 426 final Rect selfBounds = mSelfBounds; 427 final Rect overlayBounds = mOverlayBounds; 428 429 final int w = mRight-mLeft; 430 final int h = mBottom-mTop; 431 432 if (mForegroundInPadding) { 433 selfBounds.set(0, 0, w, h); 434 } else { 435 selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); 436 } 437 438 Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), 439 foreground.getIntrinsicHeight(), selfBounds, overlayBounds); 440 foreground.setBounds(overlayBounds); 441 } 442 443 foreground.draw(canvas); 444 } 445 } 446 447 /** 448 * {@inheritDoc} 449 */ 450 @Override 451 public boolean gatherTransparentRegion(Region region) { 452 boolean opaque = super.gatherTransparentRegion(region); 453 if (region != null && mForeground != null) { 454 applyDrawableToTransparentRegion(mForeground, region); 455 } 456 return opaque; 457 } 458 459 /** 460 * Sets whether to consider all children, or just those in 461 * the VISIBLE or INVISIBLE state, when measuring. Defaults to false. 462 * 463 * @param measureAll true to consider children marked GONE, false otherwise. 464 * Default value is false. 465 * 466 * @attr ref android.R.styleable#FrameLayout_measureAllChildren 467 */ 468 @android.view.RemotableViewMethod 469 public void setMeasureAllChildren(boolean measureAll) { 470 mMeasureAllChildren = measureAll; 471 } 472 473 /** 474 * Determines whether all children, or just those in the VISIBLE or 475 * INVISIBLE state, are considered when measuring. 476 * 477 * @return Whether all children are considered when measuring. 478 * 479 * @deprecated This method is deprecated in favor of 480 * {@link #getMeasureAllChildren() getMeasureAllChildren()}, which was 481 * renamed for consistency with 482 * {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}. 483 */ 484 @Deprecated 485 public boolean getConsiderGoneChildrenWhenMeasuring() { 486 return getMeasureAllChildren(); 487 } 488 489 /** 490 * Determines whether all children, or just those in the VISIBLE or 491 * INVISIBLE state, are considered when measuring. 492 * 493 * @return Whether all children are considered when measuring. 494 */ 495 public boolean getMeasureAllChildren() { 496 return mMeasureAllChildren; 497 } 498 499 /** 500 * {@inheritDoc} 501 */ 502 @Override 503 public LayoutParams generateLayoutParams(AttributeSet attrs) { 504 return new FrameLayout.LayoutParams(getContext(), attrs); 505 } 506 507 /** 508 * {@inheritDoc} 509 */ 510 @Override 511 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 512 return p instanceof LayoutParams; 513 } 514 515 @Override 516 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 517 return new LayoutParams(p); 518 } 519 520 /** 521 * Per-child layout information for layouts that support margins. 522 * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes} 523 * for a list of all child view attributes that this class supports. 524 * 525 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 526 */ 527 public static class LayoutParams extends MarginLayoutParams { 528 /** 529 * The gravity to apply with the View to which these layout parameters 530 * are associated. 531 * 532 * @see android.view.Gravity 533 * 534 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 535 */ 536 public int gravity = -1; 537 538 /** 539 * {@inheritDoc} 540 */ 541 public LayoutParams(Context c, AttributeSet attrs) { 542 super(c, attrs); 543 544 TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout); 545 gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1); 546 a.recycle(); 547 } 548 549 /** 550 * {@inheritDoc} 551 */ 552 public LayoutParams(int width, int height) { 553 super(width, height); 554 } 555 556 /** 557 * Creates a new set of layout parameters with the specified width, height 558 * and weight. 559 * 560 * @param width the width, either {@link #MATCH_PARENT}, 561 * {@link #WRAP_CONTENT} or a fixed size in pixels 562 * @param height the height, either {@link #MATCH_PARENT}, 563 * {@link #WRAP_CONTENT} or a fixed size in pixels 564 * @param gravity the gravity 565 * 566 * @see android.view.Gravity 567 */ 568 public LayoutParams(int width, int height, int gravity) { 569 super(width, height); 570 this.gravity = gravity; 571 } 572 573 /** 574 * {@inheritDoc} 575 */ 576 public LayoutParams(ViewGroup.LayoutParams source) { 577 super(source); 578 } 579 580 /** 581 * {@inheritDoc} 582 */ 583 public LayoutParams(ViewGroup.MarginLayoutParams source) { 584 super(source); 585 } 586 } 587} 588