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