FrameLayout.java revision c0053223bedf33581b0830fb87be32c1f26e5372
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.widget.RemoteViews.RemoteView; 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 START and TOP. 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.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { 128 foregroundGravity |= Gravity.START; 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 layoutDirection = getResolvedLayoutDirection(); 368 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 369 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; 370 371 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 372 case Gravity.LEFT: 373 childLeft = parentLeft + lp.leftMargin; 374 break; 375 case Gravity.CENTER_HORIZONTAL: 376 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + 377 lp.leftMargin - lp.rightMargin; 378 break; 379 case Gravity.RIGHT: 380 childLeft = parentRight - width - lp.rightMargin; 381 break; 382 default: 383 childLeft = parentLeft + lp.leftMargin; 384 } 385 386 switch (verticalGravity) { 387 case Gravity.TOP: 388 childTop = parentTop + lp.topMargin; 389 break; 390 case Gravity.CENTER_VERTICAL: 391 childTop = parentTop + (parentBottom - parentTop - height) / 2 + 392 lp.topMargin - lp.bottomMargin; 393 break; 394 case Gravity.BOTTOM: 395 childTop = parentBottom - height - lp.bottomMargin; 396 break; 397 default: 398 childTop = parentTop + lp.topMargin; 399 } 400 401 child.layout(childLeft, childTop, childLeft + width, childTop + height); 402 } 403 } 404 } 405 406 /** 407 * {@inheritDoc} 408 */ 409 @Override 410 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 411 super.onSizeChanged(w, h, oldw, oldh); 412 mForegroundBoundsChanged = true; 413 } 414 415 /** 416 * {@inheritDoc} 417 */ 418 @Override 419 public void draw(Canvas canvas) { 420 super.draw(canvas); 421 422 if (mForeground != null) { 423 final Drawable foreground = mForeground; 424 425 if (mForegroundBoundsChanged) { 426 mForegroundBoundsChanged = false; 427 final Rect selfBounds = mSelfBounds; 428 final Rect overlayBounds = mOverlayBounds; 429 430 final int w = mRight-mLeft; 431 final int h = mBottom-mTop; 432 433 if (mForegroundInPadding) { 434 selfBounds.set(0, 0, w, h); 435 } else { 436 selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); 437 } 438 439 final int layoutDirection = getResolvedLayoutDirection(); 440 Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), 441 foreground.getIntrinsicHeight(), selfBounds, overlayBounds, 442 layoutDirection); 443 foreground.setBounds(overlayBounds); 444 } 445 446 foreground.draw(canvas); 447 } 448 } 449 450 /** 451 * {@inheritDoc} 452 */ 453 @Override 454 public boolean gatherTransparentRegion(Region region) { 455 boolean opaque = super.gatherTransparentRegion(region); 456 if (region != null && mForeground != null) { 457 applyDrawableToTransparentRegion(mForeground, region); 458 } 459 return opaque; 460 } 461 462 /** 463 * Determines whether to measure all children or just those in 464 * the VISIBLE or INVISIBLE state when measuring. Defaults to false. 465 * @param measureAll true to consider children marked GONE, false otherwise. 466 * Default value is false. 467 * 468 * @attr ref android.R.styleable#FrameLayout_measureAllChildren 469 */ 470 @android.view.RemotableViewMethod 471 public void setMeasureAllChildren(boolean measureAll) { 472 mMeasureAllChildren = measureAll; 473 } 474 475 /** 476 * Determines whether to measure all children or just those in 477 * the VISIBLE or INVISIBLE state when measuring. 478 */ 479 public boolean getConsiderGoneChildrenWhenMeasuring() { 480 return mMeasureAllChildren; 481 } 482 483 /** 484 * {@inheritDoc} 485 */ 486 @Override 487 public LayoutParams generateLayoutParams(AttributeSet attrs) { 488 return new FrameLayout.LayoutParams(getContext(), attrs); 489 } 490 491 @Override 492 public boolean shouldDelayChildPressedState() { 493 return false; 494 } 495 496 /** 497 * {@inheritDoc} 498 */ 499 @Override 500 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 501 return p instanceof LayoutParams; 502 } 503 504 @Override 505 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 506 return new LayoutParams(p); 507 } 508 509 /** 510 * Per-child layout information for layouts that support margins. 511 * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes} 512 * for a list of all child view attributes that this class supports. 513 * 514 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 515 */ 516 public static class LayoutParams extends MarginLayoutParams { 517 /** 518 * The gravity to apply with the View to which these layout parameters 519 * are associated. 520 * 521 * @see android.view.Gravity 522 * 523 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 524 */ 525 public int gravity = -1; 526 527 /** 528 * {@inheritDoc} 529 */ 530 public LayoutParams(Context c, AttributeSet attrs) { 531 super(c, attrs); 532 533 TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout); 534 gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1); 535 a.recycle(); 536 } 537 538 /** 539 * {@inheritDoc} 540 */ 541 public LayoutParams(int width, int height) { 542 super(width, height); 543 } 544 545 /** 546 * Creates a new set of layout parameters with the specified width, height 547 * and weight. 548 * 549 * @param width the width, either {@link #MATCH_PARENT}, 550 * {@link #WRAP_CONTENT} or a fixed size in pixels 551 * @param height the height, either {@link #MATCH_PARENT}, 552 * {@link #WRAP_CONTENT} or a fixed size in pixels 553 * @param gravity the gravity 554 * 555 * @see android.view.Gravity 556 */ 557 public LayoutParams(int width, int height, int gravity) { 558 super(width, height); 559 this.gravity = gravity; 560 } 561 562 /** 563 * {@inheritDoc} 564 */ 565 public LayoutParams(ViewGroup.LayoutParams source) { 566 super(source); 567 } 568 569 /** 570 * {@inheritDoc} 571 */ 572 public LayoutParams(ViewGroup.MarginLayoutParams source) { 573 super(source); 574 } 575 } 576} 577