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