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 java.util.ArrayList; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.content.res.ColorStateList; 25 import android.content.res.TypedArray; 26 import android.graphics.Canvas; 27 import android.graphics.PorterDuff; 28 import android.graphics.Rect; 29 import android.graphics.Region; 30 import android.graphics.drawable.Drawable; 31 import android.util.AttributeSet; 32 import android.view.Gravity; 33 import android.view.View; 34 import android.view.ViewDebug; 35 import android.view.ViewGroup; 36 import android.view.ViewHierarchyEncoder; 37 import android.widget.RemoteViews.RemoteView; 38 39 import com.android.internal.R; 40 41 42 /** 43 * FrameLayout is designed to block out an area on the screen to display 44 * a single item. Generally, FrameLayout should be used to hold a single child view, because it can 45 * be difficult to organize child views in a way that's scalable to different screen sizes without 46 * the children overlapping each other. You can, however, add multiple children to a FrameLayout 47 * and control their position within the FrameLayout by assigning gravity to each child, using the 48 * <a href="FrameLayout.LayoutParams.html#attr_android:layout_gravity">{@code 49 * android:layout_gravity}</a> attribute. 50 * <p>Child views are drawn in a stack, with the most recently added child on top. 51 * The size of the FrameLayout is the size of its largest child (plus padding), visible 52 * or not (if the FrameLayout's parent permits). Views that are {@link android.view.View#GONE} are 53 * used for sizing 54 * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()} 55 * is set to true. 56 * 57 * @attr ref android.R.styleable#FrameLayout_measureAllChildren 58 */ 59 @RemoteView 60 public class FrameLayout extends ViewGroup { 61 private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START; 62 63 @ViewDebug.ExportedProperty(category = "measurement") 64 boolean mMeasureAllChildren = false; 65 66 @ViewDebug.ExportedProperty(category = "padding") 67 private int mForegroundPaddingLeft = 0; 68 69 @ViewDebug.ExportedProperty(category = "padding") 70 private int mForegroundPaddingTop = 0; 71 72 @ViewDebug.ExportedProperty(category = "padding") 73 private int mForegroundPaddingRight = 0; 74 75 @ViewDebug.ExportedProperty(category = "padding") 76 private int mForegroundPaddingBottom = 0; 77 78 private final Rect mSelfBounds = new Rect(); 79 private final Rect mOverlayBounds = new Rect(); 80 81 private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1); 82 FrameLayout(Context context)83 public FrameLayout(Context context) { 84 super(context); 85 } 86 FrameLayout(Context context, @Nullable AttributeSet attrs)87 public FrameLayout(Context context, @Nullable AttributeSet attrs) { 88 this(context, attrs, 0); 89 } 90 FrameLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr)91 public FrameLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 92 this(context, attrs, defStyleAttr, 0); 93 } 94 FrameLayout( Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)95 public FrameLayout( 96 Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { 97 super(context, attrs, defStyleAttr, defStyleRes); 98 99 final TypedArray a = context.obtainStyledAttributes( 100 attrs, com.android.internal.R.styleable.FrameLayout, defStyleAttr, defStyleRes); 101 102 if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) { 103 setMeasureAllChildren(true); 104 } 105 106 a.recycle(); 107 } 108 109 /** 110 * Describes how the foreground is positioned. Defaults to START and TOP. 111 * 112 * @param foregroundGravity See {@link android.view.Gravity} 113 * 114 * @see #getForegroundGravity() 115 * 116 * @attr ref android.R.styleable#View_foregroundGravity 117 */ 118 @android.view.RemotableViewMethod setForegroundGravity(int foregroundGravity)119 public void setForegroundGravity(int foregroundGravity) { 120 if (getForegroundGravity() != foregroundGravity) { 121 super.setForegroundGravity(foregroundGravity); 122 123 // calling get* again here because the set above may apply default constraints 124 final Drawable foreground = getForeground(); 125 if (getForegroundGravity() == Gravity.FILL && foreground != null) { 126 Rect padding = new Rect(); 127 if (foreground.getPadding(padding)) { 128 mForegroundPaddingLeft = padding.left; 129 mForegroundPaddingTop = padding.top; 130 mForegroundPaddingRight = padding.right; 131 mForegroundPaddingBottom = padding.bottom; 132 } 133 } else { 134 mForegroundPaddingLeft = 0; 135 mForegroundPaddingTop = 0; 136 mForegroundPaddingRight = 0; 137 mForegroundPaddingBottom = 0; 138 } 139 140 requestLayout(); 141 } 142 } 143 144 /** 145 * Returns a set of layout parameters with a width of 146 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, 147 * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}. 148 */ 149 @Override generateDefaultLayoutParams()150 protected LayoutParams generateDefaultLayoutParams() { 151 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 152 } 153 getPaddingLeftWithForeground()154 int getPaddingLeftWithForeground() { 155 return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) : 156 mPaddingLeft + mForegroundPaddingLeft; 157 } 158 getPaddingRightWithForeground()159 int getPaddingRightWithForeground() { 160 return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) : 161 mPaddingRight + mForegroundPaddingRight; 162 } 163 getPaddingTopWithForeground()164 private int getPaddingTopWithForeground() { 165 return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) : 166 mPaddingTop + mForegroundPaddingTop; 167 } 168 getPaddingBottomWithForeground()169 private int getPaddingBottomWithForeground() { 170 return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) : 171 mPaddingBottom + mForegroundPaddingBottom; 172 } 173 174 175 /** 176 * {@inheritDoc} 177 */ 178 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)179 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 180 int count = getChildCount(); 181 182 final boolean measureMatchParentChildren = 183 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || 184 MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; 185 mMatchParentChildren.clear(); 186 187 int maxHeight = 0; 188 int maxWidth = 0; 189 int childState = 0; 190 191 for (int i = 0; i < count; i++) { 192 final View child = getChildAt(i); 193 if (mMeasureAllChildren || child.getVisibility() != GONE) { 194 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); 195 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 196 maxWidth = Math.max(maxWidth, 197 child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 198 maxHeight = Math.max(maxHeight, 199 child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 200 childState = combineMeasuredStates(childState, child.getMeasuredState()); 201 if (measureMatchParentChildren) { 202 if (lp.width == LayoutParams.MATCH_PARENT || 203 lp.height == LayoutParams.MATCH_PARENT) { 204 mMatchParentChildren.add(child); 205 } 206 } 207 } 208 } 209 210 // Account for padding too 211 maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); 212 maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); 213 214 // Check against our minimum height and width 215 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 216 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 217 218 // Check against our foreground's minimum height and width 219 final Drawable drawable = getForeground(); 220 if (drawable != null) { 221 maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); 222 maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); 223 } 224 225 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 226 resolveSizeAndState(maxHeight, heightMeasureSpec, 227 childState << MEASURED_HEIGHT_STATE_SHIFT)); 228 229 count = mMatchParentChildren.size(); 230 if (count > 1) { 231 for (int i = 0; i < count; i++) { 232 final View child = mMatchParentChildren.get(i); 233 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 234 235 final int childWidthMeasureSpec; 236 if (lp.width == LayoutParams.MATCH_PARENT) { 237 final int width = Math.max(0, getMeasuredWidth() 238 - getPaddingLeftWithForeground() - getPaddingRightWithForeground() 239 - lp.leftMargin - lp.rightMargin); 240 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( 241 width, MeasureSpec.EXACTLY); 242 } else { 243 childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 244 getPaddingLeftWithForeground() + getPaddingRightWithForeground() + 245 lp.leftMargin + lp.rightMargin, 246 lp.width); 247 } 248 249 final int childHeightMeasureSpec; 250 if (lp.height == LayoutParams.MATCH_PARENT) { 251 final int height = Math.max(0, getMeasuredHeight() 252 - getPaddingTopWithForeground() - getPaddingBottomWithForeground() 253 - lp.topMargin - lp.bottomMargin); 254 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( 255 height, MeasureSpec.EXACTLY); 256 } else { 257 childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 258 getPaddingTopWithForeground() + getPaddingBottomWithForeground() + 259 lp.topMargin + lp.bottomMargin, 260 lp.height); 261 } 262 263 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 264 } 265 } 266 } 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override onLayout(boolean changed, int left, int top, int right, int bottom)272 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 273 layoutChildren(left, top, right, bottom, false /* no force left gravity */); 274 } 275 layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity)276 void layoutChildren(int left, int top, int right, int bottom, 277 boolean forceLeftGravity) { 278 final int count = getChildCount(); 279 280 final int parentLeft = getPaddingLeftWithForeground(); 281 final int parentRight = right - left - getPaddingRightWithForeground(); 282 283 final int parentTop = getPaddingTopWithForeground(); 284 final int parentBottom = bottom - top - getPaddingBottomWithForeground(); 285 286 for (int i = 0; i < count; i++) { 287 final View child = getChildAt(i); 288 if (child.getVisibility() != GONE) { 289 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 290 291 final int width = child.getMeasuredWidth(); 292 final int height = child.getMeasuredHeight(); 293 294 int childLeft; 295 int childTop; 296 297 int gravity = lp.gravity; 298 if (gravity == -1) { 299 gravity = DEFAULT_CHILD_GRAVITY; 300 } 301 302 final int layoutDirection = getLayoutDirection(); 303 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 304 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; 305 306 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 307 case Gravity.CENTER_HORIZONTAL: 308 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + 309 lp.leftMargin - lp.rightMargin; 310 break; 311 case Gravity.RIGHT: 312 if (!forceLeftGravity) { 313 childLeft = parentRight - width - lp.rightMargin; 314 break; 315 } 316 case Gravity.LEFT: 317 default: 318 childLeft = parentLeft + lp.leftMargin; 319 } 320 321 switch (verticalGravity) { 322 case Gravity.TOP: 323 childTop = parentTop + lp.topMargin; 324 break; 325 case Gravity.CENTER_VERTICAL: 326 childTop = parentTop + (parentBottom - parentTop - height) / 2 + 327 lp.topMargin - lp.bottomMargin; 328 break; 329 case Gravity.BOTTOM: 330 childTop = parentBottom - height - lp.bottomMargin; 331 break; 332 default: 333 childTop = parentTop + lp.topMargin; 334 } 335 336 child.layout(childLeft, childTop, childLeft + width, childTop + height); 337 } 338 } 339 } 340 341 /** 342 * Sets whether to consider all children, or just those in 343 * the VISIBLE or INVISIBLE state, when measuring. Defaults to false. 344 * 345 * @param measureAll true to consider children marked GONE, false otherwise. 346 * Default value is false. 347 * 348 * @attr ref android.R.styleable#FrameLayout_measureAllChildren 349 */ 350 @android.view.RemotableViewMethod setMeasureAllChildren(boolean measureAll)351 public void setMeasureAllChildren(boolean measureAll) { 352 mMeasureAllChildren = measureAll; 353 } 354 355 /** 356 * Determines whether all children, or just those in the VISIBLE or 357 * INVISIBLE state, are considered when measuring. 358 * 359 * @return Whether all children are considered when measuring. 360 * 361 * @deprecated This method is deprecated in favor of 362 * {@link #getMeasureAllChildren() getMeasureAllChildren()}, which was 363 * renamed for consistency with 364 * {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}. 365 */ 366 @Deprecated getConsiderGoneChildrenWhenMeasuring()367 public boolean getConsiderGoneChildrenWhenMeasuring() { 368 return getMeasureAllChildren(); 369 } 370 371 /** 372 * Determines whether all children, or just those in the VISIBLE or 373 * INVISIBLE state, are considered when measuring. 374 * 375 * @return Whether all children are considered when measuring. 376 */ getMeasureAllChildren()377 public boolean getMeasureAllChildren() { 378 return mMeasureAllChildren; 379 } 380 381 /** 382 * {@inheritDoc} 383 */ 384 @Override generateLayoutParams(AttributeSet attrs)385 public LayoutParams generateLayoutParams(AttributeSet attrs) { 386 return new FrameLayout.LayoutParams(getContext(), attrs); 387 } 388 389 @Override shouldDelayChildPressedState()390 public boolean shouldDelayChildPressedState() { 391 return false; 392 } 393 394 /** 395 * {@inheritDoc} 396 */ 397 @Override checkLayoutParams(ViewGroup.LayoutParams p)398 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 399 return p instanceof LayoutParams; 400 } 401 402 @Override generateLayoutParams(ViewGroup.LayoutParams p)403 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 404 return new LayoutParams(p); 405 } 406 407 @Override getAccessibilityClassName()408 public CharSequence getAccessibilityClassName() { 409 return FrameLayout.class.getName(); 410 } 411 412 /** @hide */ 413 @Override encodeProperties(@onNull ViewHierarchyEncoder encoder)414 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 415 super.encodeProperties(encoder); 416 417 encoder.addProperty("measurement:measureAllChildren", mMeasureAllChildren); 418 encoder.addProperty("padding:foregroundPaddingLeft", mForegroundPaddingLeft); 419 encoder.addProperty("padding:foregroundPaddingTop", mForegroundPaddingTop); 420 encoder.addProperty("padding:foregroundPaddingRight", mForegroundPaddingRight); 421 encoder.addProperty("padding:foregroundPaddingBottom", mForegroundPaddingBottom); 422 } 423 424 /** 425 * Per-child layout information for layouts that support margins. 426 * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes} 427 * for a list of all child view attributes that this class supports. 428 * 429 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 430 */ 431 public static class LayoutParams extends MarginLayoutParams { 432 /** 433 * The gravity to apply with the View to which these layout parameters 434 * are associated. 435 * 436 * @see android.view.Gravity 437 * 438 * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity 439 */ 440 public int gravity = -1; 441 442 /** 443 * {@inheritDoc} 444 */ LayoutParams(Context c, AttributeSet attrs)445 public LayoutParams(Context c, AttributeSet attrs) { 446 super(c, attrs); 447 448 TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout); 449 gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1); 450 a.recycle(); 451 } 452 453 /** 454 * {@inheritDoc} 455 */ LayoutParams(int width, int height)456 public LayoutParams(int width, int height) { 457 super(width, height); 458 } 459 460 /** 461 * Creates a new set of layout parameters with the specified width, height 462 * and weight. 463 * 464 * @param width the width, either {@link #MATCH_PARENT}, 465 * {@link #WRAP_CONTENT} or a fixed size in pixels 466 * @param height the height, either {@link #MATCH_PARENT}, 467 * {@link #WRAP_CONTENT} or a fixed size in pixels 468 * @param gravity the gravity 469 * 470 * @see android.view.Gravity 471 */ LayoutParams(int width, int height, int gravity)472 public LayoutParams(int width, int height, int gravity) { 473 super(width, height); 474 this.gravity = gravity; 475 } 476 477 /** 478 * {@inheritDoc} 479 */ LayoutParams(ViewGroup.LayoutParams source)480 public LayoutParams(ViewGroup.LayoutParams source) { 481 super(source); 482 } 483 484 /** 485 * {@inheritDoc} 486 */ LayoutParams(ViewGroup.MarginLayoutParams source)487 public LayoutParams(ViewGroup.MarginLayoutParams source) { 488 super(source); 489 } 490 491 /** 492 * Copy constructor. Clones the width, height, margin values, and 493 * gravity of the source. 494 * 495 * @param source The layout params to copy from. 496 */ LayoutParams(LayoutParams source)497 public LayoutParams(LayoutParams source) { 498 super(source); 499 500 this.gravity = source.gravity; 501 } 502 } 503 } 504