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