FloatingActionButton.java revision ccfe45bc4d8090bc9283f3d8dcbfc5497da273e8
1/* 2 * Copyright (C) 2015 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.support.design.widget; 18 19import android.annotation.TargetApi; 20import android.content.Context; 21import android.content.res.ColorStateList; 22import android.content.res.TypedArray; 23import android.graphics.PorterDuff; 24import android.graphics.Rect; 25import android.graphics.drawable.Drawable; 26import android.os.Build; 27import android.support.annotation.ColorInt; 28import android.support.annotation.NonNull; 29import android.support.annotation.Nullable; 30import android.support.design.R; 31import android.support.design.widget.FloatingActionButtonImpl.InternalVisibilityChangedListener; 32import android.support.v4.view.ViewCompat; 33import android.util.AttributeSet; 34import android.util.Log; 35import android.view.MotionEvent; 36import android.view.View; 37import android.widget.ImageView; 38 39import java.util.List; 40 41/** 42 * Floating action buttons are used for a special type of promoted action. They are distinguished 43 * by a circled icon floating above the UI and have special motion behaviors related to morphing, 44 * launching, and the transferring anchor point. 45 * 46 * <p>Floating action buttons come in two sizes: the default and the mini. The size can be 47 * controlled with the {@code fabSize} attribute.</p> 48 * 49 * <p>As this class descends from {@link ImageView}, you can control the icon which is displayed 50 * via {@link #setImageDrawable(Drawable)}.</p> 51 * 52 * <p>The background color of this view defaults to the your theme's {@code colorAccent}. If you 53 * wish to change this at runtime then you can do so via 54 * {@link #setBackgroundTintList(ColorStateList)}.</p> 55 * 56 * @attr ref android.support.design.R.styleable#FloatingActionButton_fabSize 57 */ 58@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class) 59public class FloatingActionButton extends VisibilityAwareImageButton { 60 61 private static final String LOG_TAG = "FloatingActionButton"; 62 63 /** 64 * Callback to be invoked when the visibility of a FloatingActionButton changes. 65 */ 66 public abstract static class OnVisibilityChangedListener { 67 /** 68 * Called when a FloatingActionButton has been 69 * {@link #show(OnVisibilityChangedListener) shown}. 70 * 71 * @param fab the FloatingActionButton that was shown. 72 */ 73 public void onShown(FloatingActionButton fab) {} 74 75 /** 76 * Called when a FloatingActionButton has been 77 * {@link #hide(OnVisibilityChangedListener) hidden}. 78 * 79 * @param fab the FloatingActionButton that was hidden. 80 */ 81 public void onHidden(FloatingActionButton fab) {} 82 } 83 84 // These values must match those in the attrs declaration 85 private static final int SIZE_MINI = 1; 86 private static final int SIZE_NORMAL = 0; 87 88 private ColorStateList mBackgroundTint; 89 private PorterDuff.Mode mBackgroundTintMode; 90 91 private int mBorderWidth; 92 private int mRippleColor; 93 private int mSize; 94 private int mImagePadding; 95 96 private boolean mCompatPadding; 97 private final Rect mShadowPadding = new Rect(); 98 private final Rect mTouchArea = new Rect(); 99 100 private FloatingActionButtonImpl mImpl; 101 102 public FloatingActionButton(Context context) { 103 this(context, null); 104 } 105 106 public FloatingActionButton(Context context, AttributeSet attrs) { 107 this(context, attrs, 0); 108 } 109 110 public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) { 111 super(context, attrs, defStyleAttr); 112 113 ThemeUtils.checkAppCompatTheme(context); 114 115 TypedArray a = context.obtainStyledAttributes(attrs, 116 R.styleable.FloatingActionButton, defStyleAttr, 117 R.style.Widget_Design_FloatingActionButton); 118 mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint); 119 mBackgroundTintMode = parseTintMode(a.getInt( 120 R.styleable.FloatingActionButton_backgroundTintMode, -1), null); 121 mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0); 122 mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_NORMAL); 123 mBorderWidth = a.getDimensionPixelSize(R.styleable.FloatingActionButton_borderWidth, 0); 124 final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f); 125 final float pressedTranslationZ = a.getDimension( 126 R.styleable.FloatingActionButton_pressedTranslationZ, 0f); 127 mCompatPadding = a.getBoolean(R.styleable.FloatingActionButton_useCompatPadding, false); 128 a.recycle(); 129 130 final int maxImageSize = (int) getResources().getDimension(R.dimen.design_fab_image_size); 131 mImagePadding = (getSizeDimension() - maxImageSize) / 2; 132 133 getImpl().setBackgroundDrawable(mBackgroundTint, mBackgroundTintMode, 134 mRippleColor, mBorderWidth); 135 getImpl().setElevation(elevation); 136 getImpl().setPressedTranslationZ(pressedTranslationZ); 137 getImpl().updatePadding(); 138 } 139 140 @Override 141 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 142 final int preferredSize = getSizeDimension(); 143 144 final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec); 145 final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec); 146 147 // As we want to stay circular, we set both dimensions to be the 148 // smallest resolved dimension 149 final int d = Math.min(w, h); 150 151 // We add the shadow's padding to the measured dimension 152 setMeasuredDimension( 153 d + mShadowPadding.left + mShadowPadding.right, 154 d + mShadowPadding.top + mShadowPadding.bottom); 155 } 156 157 /** 158 * Set the ripple color for this {@link FloatingActionButton}. 159 * <p> 160 * When running on devices with KitKat or below, we draw a fill rather than a ripple. 161 * 162 * @param color ARGB color to use for the ripple. 163 * 164 * @attr ref android.support.design.R.styleable#FloatingActionButton_rippleColor 165 */ 166 public void setRippleColor(@ColorInt int color) { 167 if (mRippleColor != color) { 168 mRippleColor = color; 169 getImpl().setRippleColor(color); 170 } 171 } 172 173 /** 174 * Return the tint applied to the background drawable, if specified. 175 * 176 * @return the tint applied to the background drawable 177 * @see #setBackgroundTintList(ColorStateList) 178 */ 179 @Nullable 180 @Override 181 public ColorStateList getBackgroundTintList() { 182 return mBackgroundTint; 183 } 184 185 /** 186 * Applies a tint to the background drawable. Does not modify the current tint 187 * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 188 * 189 * @param tint the tint to apply, may be {@code null} to clear tint 190 */ 191 public void setBackgroundTintList(@Nullable ColorStateList tint) { 192 if (mBackgroundTint != tint) { 193 mBackgroundTint = tint; 194 getImpl().setBackgroundTintList(tint); 195 } 196 } 197 198 /** 199 * Return the blending mode used to apply the tint to the background 200 * drawable, if specified. 201 * 202 * @return the blending mode used to apply the tint to the background 203 * drawable 204 * @see #setBackgroundTintMode(PorterDuff.Mode) 205 */ 206 @Nullable 207 @Override 208 public PorterDuff.Mode getBackgroundTintMode() { 209 return mBackgroundTintMode; 210 } 211 212 /** 213 * Specifies the blending mode used to apply the tint specified by 214 * {@link #setBackgroundTintList(ColorStateList)}} to the background 215 * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 216 * 217 * @param tintMode the blending mode used to apply the tint, may be 218 * {@code null} to clear tint 219 */ 220 public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 221 if (mBackgroundTintMode != tintMode) { 222 mBackgroundTintMode = tintMode; 223 getImpl().setBackgroundTintMode(tintMode); 224 } 225 } 226 227 @Override 228 public void setBackgroundDrawable(Drawable background) { 229 Log.i(LOG_TAG, "Setting a custom background is not supported."); 230 } 231 232 @Override 233 public void setBackgroundResource(int resid) { 234 Log.i(LOG_TAG, "Setting a custom background is not supported."); 235 } 236 237 @Override 238 public void setBackgroundColor(int color) { 239 Log.i(LOG_TAG, "Setting a custom background is not supported."); 240 } 241 242 /** 243 * Shows the button. 244 * <p>This method will animate the button show if the view has already been laid out.</p> 245 */ 246 public void show() { 247 show(null); 248 } 249 250 /** 251 * Shows the button. 252 * <p>This method will animate the button show if the view has already been laid out.</p> 253 * 254 * @param listener the listener to notify when this view is shown 255 */ 256 public void show(@Nullable final OnVisibilityChangedListener listener) { 257 show(listener, true); 258 } 259 260 private void show(OnVisibilityChangedListener listener, boolean fromUser) { 261 getImpl().show(wrapOnVisibilityChangedListener(listener), fromUser); 262 } 263 264 /** 265 * Hides the button. 266 * <p>This method will animate the button hide if the view has already been laid out.</p> 267 */ 268 public void hide() { 269 hide(null); 270 } 271 272 /** 273 * Hides the button. 274 * <p>This method will animate the button hide if the view has already been laid out.</p> 275 * 276 * @param listener the listener to notify when this view is hidden 277 */ 278 public void hide(@Nullable OnVisibilityChangedListener listener) { 279 hide(listener, true); 280 } 281 282 private void hide(@Nullable OnVisibilityChangedListener listener, boolean fromUser) { 283 getImpl().hide(wrapOnVisibilityChangedListener(listener), fromUser); 284 } 285 286 /** 287 * Set whether FloatingActionButton should add inner padding on platforms Lollipop and after, 288 * to ensure consistent dimensions on all platforms. 289 * 290 * @param useCompatPadding true if FloatingActionButton is adding inner padding on platforms 291 * Lollipop and after, to ensure consistent dimensions on all platforms. 292 * 293 * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding 294 * @see #getUseCompatPadding() 295 */ 296 public void setUseCompatPadding(boolean useCompatPadding) { 297 if (mCompatPadding != useCompatPadding) { 298 mCompatPadding = useCompatPadding; 299 getImpl().onCompatShadowChanged(); 300 } 301 } 302 303 /** 304 * Returns whether FloatingActionButton will add inner padding on platforms Lollipop and after. 305 * 306 * @return true if FloatingActionButton is adding inner padding on platforms Lollipop and after, 307 * to ensure consistent dimensions on all platforms. 308 * 309 * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding 310 * @see #setUseCompatPadding(boolean) 311 */ 312 public boolean getUseCompatPadding() { 313 return mCompatPadding; 314 } 315 316 @Nullable 317 private InternalVisibilityChangedListener wrapOnVisibilityChangedListener( 318 @Nullable final OnVisibilityChangedListener listener) { 319 if (listener == null) { 320 return null; 321 } 322 323 return new InternalVisibilityChangedListener() { 324 @Override 325 public void onShown() { 326 listener.onShown(FloatingActionButton.this); 327 } 328 329 @Override 330 public void onHidden() { 331 listener.onHidden(FloatingActionButton.this); 332 } 333 }; 334 } 335 336 final int getSizeDimension() { 337 switch (mSize) { 338 case SIZE_MINI: 339 return getResources().getDimensionPixelSize(R.dimen.design_fab_size_mini); 340 case SIZE_NORMAL: 341 default: 342 return getResources().getDimensionPixelSize(R.dimen.design_fab_size_normal); 343 } 344 } 345 346 @Override 347 protected void onAttachedToWindow() { 348 super.onAttachedToWindow(); 349 getImpl().onAttachedToWindow(); 350 } 351 352 @Override 353 protected void onDetachedFromWindow() { 354 super.onDetachedFromWindow(); 355 getImpl().onDetachedFromWindow(); 356 } 357 358 @Override 359 protected void drawableStateChanged() { 360 super.drawableStateChanged(); 361 getImpl().onDrawableStateChanged(getDrawableState()); 362 } 363 364 @TargetApi(Build.VERSION_CODES.HONEYCOMB) 365 @Override 366 public void jumpDrawablesToCurrentState() { 367 super.jumpDrawablesToCurrentState(); 368 getImpl().jumpDrawableToCurrentState(); 369 } 370 371 /** 372 * Return in {@code rect} the bounds of the actual floating action button content in view-local 373 * coordinates. This is defined as anything within any visible shadow. 374 * 375 * @return true if this view actually has been laid out and has a content rect, else false. 376 */ 377 public boolean getContentRect(@NonNull Rect rect) { 378 if (ViewCompat.isLaidOut(this)) { 379 rect.set(0, 0, getWidth(), getHeight()); 380 rect.left += mShadowPadding.left; 381 rect.top += mShadowPadding.top; 382 rect.right -= mShadowPadding.right; 383 rect.bottom -= mShadowPadding.bottom; 384 return true; 385 } else { 386 return false; 387 } 388 } 389 390 /** 391 * Returns the FloatingActionButton's background, minus any compatible shadow implementation. 392 */ 393 @NonNull 394 public Drawable getContentBackground() { 395 return getImpl().getContentBackground(); 396 } 397 398 private static int resolveAdjustedSize(int desiredSize, int measureSpec) { 399 int result = desiredSize; 400 int specMode = MeasureSpec.getMode(measureSpec); 401 int specSize = MeasureSpec.getSize(measureSpec); 402 switch (specMode) { 403 case MeasureSpec.UNSPECIFIED: 404 // Parent says we can be as big as we want. Just don't be larger 405 // than max size imposed on ourselves. 406 result = desiredSize; 407 break; 408 case MeasureSpec.AT_MOST: 409 // Parent says we can be as big as we want, up to specSize. 410 // Don't be larger than specSize, and don't be larger than 411 // the max size imposed on ourselves. 412 result = Math.min(desiredSize, specSize); 413 break; 414 case MeasureSpec.EXACTLY: 415 // No choice. Do what we are told. 416 result = specSize; 417 break; 418 } 419 return result; 420 } 421 422 static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) { 423 switch (value) { 424 case 3: 425 return PorterDuff.Mode.SRC_OVER; 426 case 5: 427 return PorterDuff.Mode.SRC_IN; 428 case 9: 429 return PorterDuff.Mode.SRC_ATOP; 430 case 14: 431 return PorterDuff.Mode.MULTIPLY; 432 case 15: 433 return PorterDuff.Mode.SCREEN; 434 default: 435 return defaultMode; 436 } 437 } 438 439 @Override 440 public boolean onTouchEvent(MotionEvent ev) { 441 if(getContentRect(mTouchArea) && !mTouchArea.contains((int) ev.getX(), (int) ev.getY())) { 442 return false; 443 } 444 445 return super.onTouchEvent(ev); 446 } 447 448 /** 449 * Behavior designed for use with {@link FloatingActionButton} instances. Its main function 450 * is to move {@link FloatingActionButton} views so that any displayed {@link Snackbar}s do 451 * not cover them. 452 */ 453 public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> { 454 // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is 455 // because we can use view translation properties which greatly simplifies the code. 456 private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11; 457 458 private ValueAnimatorCompat mFabTranslationYAnimator; 459 private float mFabTranslationY; 460 private Rect mTmpRect; 461 462 @Override 463 public boolean layoutDependsOn(CoordinatorLayout parent, 464 FloatingActionButton child, View dependency) { 465 // We're dependent on all SnackbarLayouts (if enabled) 466 return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout; 467 } 468 469 @Override 470 public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, 471 View dependency) { 472 if (dependency instanceof Snackbar.SnackbarLayout) { 473 updateFabTranslationForSnackbar(parent, child, dependency); 474 } else if (dependency instanceof AppBarLayout) { 475 // If we're depending on an AppBarLayout we will show/hide it automatically 476 // if the FAB is anchored to the AppBarLayout 477 updateFabVisibility(parent, (AppBarLayout) dependency, child); 478 } 479 return false; 480 } 481 482 private boolean updateFabVisibility(CoordinatorLayout parent, 483 AppBarLayout appBarLayout, FloatingActionButton child) { 484 final CoordinatorLayout.LayoutParams lp = 485 (CoordinatorLayout.LayoutParams) child.getLayoutParams(); 486 if (lp.getAnchorId() != appBarLayout.getId()) { 487 // The anchor ID doesn't match the dependency, so we won't automatically 488 // show/hide the FAB 489 return false; 490 } 491 492 if (child.getUserSetVisibility() != VISIBLE) { 493 // The view isn't set to be visible so skip changing its visibility 494 return false; 495 } 496 497 if (mTmpRect == null) { 498 mTmpRect = new Rect(); 499 } 500 501 // First, let's get the visible rect of the dependency 502 final Rect rect = mTmpRect; 503 ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect); 504 505 if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) { 506 // If the anchor's bottom is below the seam, we'll animate our FAB out 507 child.hide(null, false); 508 } else { 509 // Else, we'll animate our FAB back in 510 child.show(null, false); 511 } 512 return true; 513 } 514 515 private void updateFabTranslationForSnackbar(CoordinatorLayout parent, 516 final FloatingActionButton fab, View snackbar) { 517 final float targetTransY = getFabTranslationYForSnackbar(parent, fab); 518 if (mFabTranslationY == targetTransY) { 519 // We're already at (or currently animating to) the target value, return... 520 return; 521 } 522 523 final float currentTransY = ViewCompat.getTranslationY(fab); 524 525 // Make sure that any current animation is cancelled 526 if (mFabTranslationYAnimator != null && mFabTranslationYAnimator.isRunning()) { 527 mFabTranslationYAnimator.cancel(); 528 } 529 530 if (fab.isShown() 531 && Math.abs(currentTransY - targetTransY) > (fab.getHeight() * 0.667f)) { 532 // If the FAB will be travelling by more than 2/3 of its height, let's animate 533 // it instead 534 if (mFabTranslationYAnimator == null) { 535 mFabTranslationYAnimator = ViewUtils.createAnimator(); 536 mFabTranslationYAnimator.setInterpolator( 537 AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR); 538 mFabTranslationYAnimator.setUpdateListener( 539 new ValueAnimatorCompat.AnimatorUpdateListener() { 540 @Override 541 public void onAnimationUpdate(ValueAnimatorCompat animator) { 542 ViewCompat.setTranslationY(fab, 543 animator.getAnimatedFloatValue()); 544 } 545 }); 546 } 547 mFabTranslationYAnimator.setFloatValues(currentTransY, targetTransY); 548 mFabTranslationYAnimator.start(); 549 } else { 550 // Now update the translation Y 551 ViewCompat.setTranslationY(fab, targetTransY); 552 } 553 554 mFabTranslationY = targetTransY; 555 } 556 557 private float getFabTranslationYForSnackbar(CoordinatorLayout parent, 558 FloatingActionButton fab) { 559 float minOffset = 0; 560 final List<View> dependencies = parent.getDependencies(fab); 561 for (int i = 0, z = dependencies.size(); i < z; i++) { 562 final View view = dependencies.get(i); 563 if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) { 564 minOffset = Math.min(minOffset, 565 ViewCompat.getTranslationY(view) - view.getHeight()); 566 } 567 } 568 569 return minOffset; 570 } 571 572 @Override 573 public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child, 574 int layoutDirection) { 575 // First, let's make sure that the visibility of the FAB is consistent 576 final List<View> dependencies = parent.getDependencies(child); 577 for (int i = 0, count = dependencies.size(); i < count; i++) { 578 final View dependency = dependencies.get(i); 579 if (dependency instanceof AppBarLayout 580 && updateFabVisibility(parent, (AppBarLayout) dependency, child)) { 581 break; 582 } 583 } 584 // Now let the CoordinatorLayout lay out the FAB 585 parent.onLayoutChild(child, layoutDirection); 586 // Now offset it if needed 587 offsetIfNeeded(parent, child); 588 return true; 589 } 590 591 /** 592 * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method 593 * offsets our layout position so that we're positioned correctly if we're on one of 594 * our parent's edges. 595 */ 596 private void offsetIfNeeded(CoordinatorLayout parent, FloatingActionButton fab) { 597 final Rect padding = fab.mShadowPadding; 598 599 if (padding != null && padding.centerX() > 0 && padding.centerY() > 0) { 600 final CoordinatorLayout.LayoutParams lp = 601 (CoordinatorLayout.LayoutParams) fab.getLayoutParams(); 602 603 int offsetTB = 0, offsetLR = 0; 604 605 if (fab.getRight() >= parent.getWidth() - lp.rightMargin) { 606 // If we're on the left edge, shift it the right 607 offsetLR = padding.right; 608 } else if (fab.getLeft() <= lp.leftMargin) { 609 // If we're on the left edge, shift it the left 610 offsetLR = -padding.left; 611 } 612 if (fab.getBottom() >= parent.getBottom() - lp.bottomMargin) { 613 // If we're on the bottom edge, shift it down 614 offsetTB = padding.bottom; 615 } else if (fab.getTop() <= lp.topMargin) { 616 // If we're on the top edge, shift it up 617 offsetTB = -padding.top; 618 } 619 620 fab.offsetTopAndBottom(offsetTB); 621 fab.offsetLeftAndRight(offsetLR); 622 } 623 } 624 } 625 626 /** 627 * Returns the backward compatible elevation of the FloatingActionButton. 628 * 629 * @return the backward compatible elevation in pixels. 630 * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation 631 * @see #setCompatElevation(float) 632 */ 633 public float getCompatElevation() { 634 return getImpl().getElevation(); 635 } 636 637 /** 638 * Updates the backward compatible elevation of the FloatingActionButton. 639 * 640 * @param elevation The backward compatible elevation in pixels. 641 * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation 642 * @see #getCompatElevation() 643 * @see #setUseCompatPadding(boolean) 644 */ 645 public void setCompatElevation(float elevation) { 646 getImpl().setElevation(elevation); 647 } 648 649 private FloatingActionButtonImpl getImpl() { 650 if (mImpl == null) { 651 mImpl = createImpl(); 652 } 653 return mImpl; 654 } 655 656 private FloatingActionButtonImpl createImpl() { 657 final int sdk = Build.VERSION.SDK_INT; 658 if (sdk >= 21) { 659 return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl()); 660 } else if (sdk >= 14) { 661 return new FloatingActionButtonIcs(this, new ShadowDelegateImpl()); 662 } else { 663 return new FloatingActionButtonEclairMr1(this, new ShadowDelegateImpl()); 664 } 665 } 666 667 private class ShadowDelegateImpl implements ShadowViewDelegate { 668 @Override 669 public float getRadius() { 670 return getSizeDimension() / 2f; 671 } 672 673 @Override 674 public void setShadowPadding(int left, int top, int right, int bottom) { 675 mShadowPadding.set(left, top, right, bottom); 676 setPadding(left + mImagePadding, top + mImagePadding, 677 right + mImagePadding, bottom + mImagePadding); 678 } 679 680 @Override 681 public void setBackgroundDrawable(Drawable background) { 682 FloatingActionButton.super.setBackgroundDrawable(background); 683 } 684 685 @Override 686 public boolean isCompatPaddingEnabled() { 687 return mCompatPadding; 688 } 689 } 690} 691