FrameLayout.java revision 9c9573721140c260d4614c7ac58923dcfa0cea22
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) setConsiderGoneChildrenWhenMeasuring()} 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 maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); 269 maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); 270 childState = combineMeasuredStates(childState, child.getMeasuredState()); 271 if (measureMatchParentChildren) { 272 final ViewGroup.LayoutParams lp = child.getLayoutParams(); 273 if (lp.width == LayoutParams.MATCH_PARENT || 274 lp.height == LayoutParams.MATCH_PARENT) { 275 mMatchParentChildren.add(child); 276 } 277 } 278 } 279 } 280 281 // Account for padding too 282 maxWidth += mPaddingLeft + mPaddingRight + mForegroundPaddingLeft + mForegroundPaddingRight; 283 maxHeight += mPaddingTop + mPaddingBottom + mForegroundPaddingTop + mForegroundPaddingBottom; 284 285 // Check against our minimum height and width 286 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 287 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 288 289 // Check against our foreground's minimum height and width 290 final Drawable drawable = getForeground(); 291 if (drawable != null) { 292 maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); 293 maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); 294 } 295 296 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 297 resolveSizeAndState(maxHeight, heightMeasureSpec, 298 childState << MEASURED_HEIGHT_STATE_SHIFT)); 299 300 if (mMatchParentChildren.size() > 0) { 301 count = mMatchParentChildren.size(); 302 for (int i = 0; i < count; i++) { 303 final View child = mMatchParentChildren.get(i); 304 305 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 306 int childWidthMeasureSpec; 307 int childHeightMeasureSpec; 308 309 if (lp.width == LayoutParams.MATCH_PARENT) { 310 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - 311 mPaddingLeft - mPaddingRight - lp.leftMargin - lp.rightMargin, 312 MeasureSpec.EXACTLY); 313 } else { 314 childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 315 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin, 316 lp.width); 317 } 318 319 if (lp.height == LayoutParams.MATCH_PARENT) { 320 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - 321 mPaddingTop - mPaddingBottom - lp.topMargin - lp.bottomMargin, 322 MeasureSpec.EXACTLY); 323 } else { 324 childHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 325 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin, 326 lp.height); 327 } 328 329 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 330 } 331 } 332 } 333 334 /** 335 * {@inheritDoc} 336 */ 337 @Override 338 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 339 final int count = getChildCount(); 340 341 final int parentLeft = mPaddingLeft + mForegroundPaddingLeft; 342 final int parentRight = right - left - mPaddingRight - mForegroundPaddingRight; 343 344 final int parentTop = mPaddingTop + mForegroundPaddingTop; 345 final int parentBottom = bottom - top - mPaddingBottom - mForegroundPaddingBottom; 346 347 mForegroundBoundsChanged = true; 348 349 for (int i = 0; i < count; i++) { 350 final View child = getChildAt(i); 351 if (child.getVisibility() != GONE) { 352 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 353 354 final int width = child.getMeasuredWidth(); 355 final int height = child.getMeasuredHeight(); 356 357 int childLeft; 358 int childTop; 359 360 int gravity = lp.gravity; 361 if (gravity == -1) { 362 gravity = DEFAULT_CHILD_GRAVITY; 363 } 364 365 final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; 366 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; 367 368 switch (horizontalGravity) { 369 case Gravity.LEFT: 370 childLeft = parentLeft + lp.leftMargin; 371 break; 372 case Gravity.CENTER_HORIZONTAL: 373 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + 374 lp.leftMargin - lp.rightMargin; 375 break; 376 case Gravity.RIGHT: 377 childLeft = parentRight - width - lp.rightMargin; 378 break; 379 default: 380 childLeft = parentLeft + lp.leftMargin; 381 } 382 383 switch (verticalGravity) { 384 case Gravity.TOP: 385 childTop = parentTop + lp.topMargin; 386 break; 387 case Gravity.CENTER_VERTICAL: 388 childTop = parentTop + (parentBottom - parentTop - height) / 2 + 389 lp.topMargin - lp.bottomMargin; 390 break; 391 case Gravity.BOTTOM: 392 childTop = parentBottom - height - lp.bottomMargin; 393 break; 394 default: 395 childTop = parentTop + lp.topMargin; 396 } 397 398 child.layout(childLeft, childTop, childLeft + width, childTop + height); 399 } 400 } 401 } 402 403 /** 404 * {@inheritDoc} 405 */ 406 @Override 407 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 408 super.onSizeChanged(w, h, oldw, oldh); 409 mForegroundBoundsChanged = true; 410 } 411 412 /** 413 * {@inheritDoc} 414 */ 415 @Override 416 public void draw(Canvas canvas) { 417 super.draw(canvas); 418 419 if (mForeground != null) { 420 final Drawable foreground = mForeground; 421 422 if (mForegroundBoundsChanged) { 423 mForegroundBoundsChanged = false; 424 final Rect selfBounds = mSelfBounds; 425 final Rect overlayBounds = mOverlayBounds; 426 427 final int w = mRight-mLeft; 428 final int h = mBottom-mTop; 429 430 if (mForegroundInPadding) { 431 selfBounds.set(0, 0, w, h); 432 } else { 433 selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); 434 } 435 436 Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), 437 foreground.getIntrinsicHeight(), selfBounds, overlayBounds); 438 foreground.setBounds(overlayBounds); 439 } 440 441 foreground.draw(canvas); 442 } 443 } 444 445 /** 446 * {@inheritDoc} 447 */ 448 @Override 449 public boolean gatherTransparentRegion(Region region) { 450 boolean opaque = super.gatherTransparentRegion(region); 451 if (region != null && mForeground != null) { 452 applyDrawableToTransparentRegion(mForeground, region); 453 } 454 return opaque; 455 } 456 457 /** 458 * Determines whether to measure all children or just those in 459 * the VISIBLE or INVISIBLE state when measuring. Defaults to false. 460 * @param measureAll true to consider children marked GONE, false otherwise. 461 * Default value is false. 462 * 463 * @attr ref android.R.styleable#FrameLayout_measureAllChildren 464 */ 465 @android.view.RemotableViewMethod 466 public void setMeasureAllChildren(boolean measureAll) { 467 mMeasureAllChildren = measureAll; 468 } 469 470 /** 471 * Determines whether to measure all children or just those in 472 * the VISIBLE or INVISIBLE state when measuring. 473 */ 474 public boolean getConsiderGoneChildrenWhenMeasuring() { 475 return mMeasureAllChildren; 476 } 477 478 /** 479 * {@inheritDoc} 480 */ 481 @Override 482 public LayoutParams generateLayoutParams(AttributeSet attrs) { 483 return new FrameLayout.LayoutParams(getContext(), attrs); 484 } 485 486 /** 487 * {@inheritDoc} 488 */ 489 @Override 490 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 491 return p instanceof LayoutParams; 492 } 493 494 @Override 495 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 496 return new LayoutParams(p); 497 } 498 499 /** 500 * Per-child layout information for layouts that support margins. 501 * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes} 502 * for a list of all child view attributes that this class supports. 503 * 504 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 505 */ 506 public static class LayoutParams extends MarginLayoutParams { 507 /** 508 * The gravity to apply with the View to which these layout parameters 509 * are associated. 510 * 511 * @see android.view.Gravity 512 * 513 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 514 */ 515 public int gravity = -1; 516 517 /** 518 * {@inheritDoc} 519 */ 520 public LayoutParams(Context c, AttributeSet attrs) { 521 super(c, attrs); 522 523 TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout); 524 gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1); 525 a.recycle(); 526 } 527 528 /** 529 * {@inheritDoc} 530 */ 531 public LayoutParams(int width, int height) { 532 super(width, height); 533 } 534 535 /** 536 * Creates a new set of layout parameters with the specified width, height 537 * and weight. 538 * 539 * @param width the width, either {@link #MATCH_PARENT}, 540 * {@link #WRAP_CONTENT} or a fixed size in pixels 541 * @param height the height, either {@link #MATCH_PARENT}, 542 * {@link #WRAP_CONTENT} or a fixed size in pixels 543 * @param gravity the gravity 544 * 545 * @see android.view.Gravity 546 */ 547 public LayoutParams(int width, int height, int gravity) { 548 super(width, height); 549 this.gravity = gravity; 550 } 551 552 /** 553 * {@inheritDoc} 554 */ 555 public LayoutParams(ViewGroup.LayoutParams source) { 556 super(source); 557 } 558 559 /** 560 * {@inheritDoc} 561 */ 562 public LayoutParams(ViewGroup.MarginLayoutParams source) { 563 super(source); 564 } 565 } 566} 567 568