FrameLayout.java revision e0a799a2ac1ca78e30fbac9e4e12a063425c08d3
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 * Determines whether to measure all children or just those in 461 * the VISIBLE or INVISIBLE state when measuring. Defaults to false. 462 * @param measureAll true to consider children marked GONE, false otherwise. 463 * Default value is false. 464 * 465 * @attr ref android.R.styleable#FrameLayout_measureAllChildren 466 */ 467 @android.view.RemotableViewMethod 468 public void setMeasureAllChildren(boolean measureAll) { 469 mMeasureAllChildren = measureAll; 470 } 471 472 /** 473 * Determines whether to measure all children or just those in 474 * the VISIBLE or INVISIBLE state when measuring. 475 */ 476 public boolean getConsiderGoneChildrenWhenMeasuring() { 477 return mMeasureAllChildren; 478 } 479 480 /** 481 * {@inheritDoc} 482 */ 483 @Override 484 public LayoutParams generateLayoutParams(AttributeSet attrs) { 485 return new FrameLayout.LayoutParams(getContext(), attrs); 486 } 487 488 @Override 489 public boolean shouldDelayChildPressedState() { 490 return false; 491 } 492 493 /** 494 * {@inheritDoc} 495 */ 496 @Override 497 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 498 return p instanceof LayoutParams; 499 } 500 501 @Override 502 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 503 return new LayoutParams(p); 504 } 505 506 /** 507 * Per-child layout information for layouts that support margins. 508 * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes} 509 * for a list of all child view attributes that this class supports. 510 * 511 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 512 */ 513 public static class LayoutParams extends MarginLayoutParams { 514 /** 515 * The gravity to apply with the View to which these layout parameters 516 * are associated. 517 * 518 * @see android.view.Gravity 519 * 520 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 521 */ 522 public int gravity = -1; 523 524 /** 525 * {@inheritDoc} 526 */ 527 public LayoutParams(Context c, AttributeSet attrs) { 528 super(c, attrs); 529 530 TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout); 531 gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1); 532 a.recycle(); 533 } 534 535 /** 536 * {@inheritDoc} 537 */ 538 public LayoutParams(int width, int height) { 539 super(width, height); 540 } 541 542 /** 543 * Creates a new set of layout parameters with the specified width, height 544 * and weight. 545 * 546 * @param width the width, either {@link #MATCH_PARENT}, 547 * {@link #WRAP_CONTENT} or a fixed size in pixels 548 * @param height the height, either {@link #MATCH_PARENT}, 549 * {@link #WRAP_CONTENT} or a fixed size in pixels 550 * @param gravity the gravity 551 * 552 * @see android.view.Gravity 553 */ 554 public LayoutParams(int width, int height, int gravity) { 555 super(width, height); 556 this.gravity = gravity; 557 } 558 559 /** 560 * {@inheritDoc} 561 */ 562 public LayoutParams(ViewGroup.LayoutParams source) { 563 super(source); 564 } 565 566 /** 567 * {@inheritDoc} 568 */ 569 public LayoutParams(ViewGroup.MarginLayoutParams source) { 570 super(source); 571 } 572 } 573} 574