Add compat padding to FloatingActionButton ala CardView
Defaults to disabled, but when enabled, FloatingActionButton
will have the same metrics on both Lollipop+, and older
platforms where the compat shadow is used.
BUG: 25274672
Change-Id: Ide28651124ab31472c588e7d65a32999ec674445
diff --git a/design/api/current.txt b/design/api/current.txt
index b808dba..216100540 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -224,9 +224,13 @@
ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
method public android.graphics.drawable.Drawable getContentBackground();
method public boolean getContentRect(android.graphics.Rect);
+ method public float getFloatingActionButtonElevation();
+ method public boolean getUseCompatPadding();
method public void hide();
method public void hide(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
+ method public void setFloatingActionButtonElevation(float);
method public void setRippleColor(int);
+ method public void setUseCompatPadding(boolean);
method public void show();
method public void show(android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener);
}
diff --git a/design/base/android/support/design/widget/FloatingActionButtonImpl.java b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
index 169eaa6..189060a 100644
--- a/design/base/android/support/design/widget/FloatingActionButtonImpl.java
+++ b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
@@ -20,6 +20,7 @@
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PorterDuff;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.support.annotation.Nullable;
@@ -52,6 +53,7 @@
final VisibilityAwareImageButton mView;
final ShadowViewDelegate mShadowViewDelegate;
+ private final Rect mTmpRect = new Rect();
private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
FloatingActionButtonImpl(VisibilityAwareImageButton view,
@@ -76,6 +78,8 @@
}
}
+ abstract float getElevation();
+
final void setPressedTranslationZ(float translationZ) {
if (mPressedTranslationZ != translationZ) {
mPressedTranslationZ = translationZ;
@@ -99,6 +103,19 @@
return mContentBackground;
}
+ abstract void onCompatShadowChanged();
+
+ final void updatePadding() {
+ Rect rect = mTmpRect;
+ getPadding(rect);
+ onPaddingUpdated(rect);
+ mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom);
+ }
+
+ abstract void getPadding(Rect rect);
+
+ void onPaddingUpdated(Rect padding) {}
+
void onAttachedToWindow() {
if (requirePreDrawListener()) {
ensurePreDrawListener();
diff --git a/design/base/android/support/design/widget/ShadowViewDelegate.java b/design/base/android/support/design/widget/ShadowViewDelegate.java
index 9a395e6..83a3a7a 100644
--- a/design/base/android/support/design/widget/ShadowViewDelegate.java
+++ b/design/base/android/support/design/widget/ShadowViewDelegate.java
@@ -22,4 +22,5 @@
float getRadius();
void setShadowPadding(int left, int top, int right, int bottom);
void setBackgroundDrawable(Drawable background);
+ boolean isCompatPaddingEnabled();
}
diff --git a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
index 0415d33..92f9603 100644
--- a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
+++ b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
@@ -96,10 +96,7 @@
mElevation,
mElevation + mPressedTranslationZ);
mShadowDrawable.setAddPaddingForCorners(false);
-
mShadowViewDelegate.setBackgroundDrawable(mShadowDrawable);
-
- updatePadding();
}
@Override
@@ -121,6 +118,11 @@
}
@Override
+ float getElevation() {
+ return mElevation;
+ }
+
+ @Override
void onElevationChanged(float elevation) {
if (mShadowDrawable != null) {
mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ);
@@ -205,10 +207,13 @@
}
}
- private void updatePadding() {
- Rect rect = new Rect();
+ @Override
+ void onCompatShadowChanged() {
+ // Ignore pre-v21
+ }
+
+ void getPadding(Rect rect) {
mShadowDrawable.getPadding(rect);
- mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom);
}
private Animation setupAnimation(Animation animation) {
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index cc3aca9..2b85845 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -22,7 +22,9 @@
import android.annotation.TargetApi;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
@@ -35,6 +37,7 @@
class FloatingActionButtonLollipop extends FloatingActionButtonIcs {
private final Interpolator mInterpolator;
+ private InsetDrawable mInsetDrawable;
FloatingActionButtonLollipop(VisibilityAwareImageButton view,
ShadowViewDelegate shadowViewDelegate) {
@@ -70,7 +73,6 @@
mContentBackground = mRippleDrawable;
mShadowViewDelegate.setBackgroundDrawable(mRippleDrawable);
- mShadowViewDelegate.setShadowPadding(0, 0, 0, 0);
}
@Override
@@ -84,13 +86,15 @@
@Override
public void onElevationChanged(float elevation) {
- ViewCompat.setElevation(mView, elevation);
+ mView.setElevation(elevation);
+ if (mShadowViewDelegate.isCompatPaddingEnabled()) {
+ updatePadding();
+ }
}
@Override
void onTranslationZChanged(float translationZ) {
StateListAnimator stateListAnimator = new StateListAnimator();
-
// Animate translationZ to our value when pressed or focused
stateListAnimator.addState(PRESSED_ENABLED_STATE_SET,
setupAnimator(ObjectAnimator.ofFloat(mView, "translationZ", translationZ)));
@@ -99,8 +103,32 @@
// Animate translationZ to 0 otherwise
stateListAnimator.addState(EMPTY_STATE_SET,
setupAnimator(ObjectAnimator.ofFloat(mView, "translationZ", 0f)));
-
mView.setStateListAnimator(stateListAnimator);
+
+ if (mShadowViewDelegate.isCompatPaddingEnabled()) {
+ updatePadding();
+ }
+ }
+
+ @Override
+ public float getElevation() {
+ return mView.getElevation();
+ }
+
+ @Override
+ void onCompatShadowChanged() {
+ updatePadding();
+ }
+
+ @Override
+ void onPaddingUpdated(Rect padding) {
+ if (mShadowViewDelegate.isCompatPaddingEnabled()) {
+ mInsetDrawable = new InsetDrawable(mRippleDrawable,
+ padding.left, padding.top, padding.right, padding.bottom);
+ mShadowViewDelegate.setBackgroundDrawable(mInsetDrawable);
+ } else {
+ mShadowViewDelegate.setBackgroundDrawable(mRippleDrawable);
+ }
}
@Override
@@ -127,4 +155,18 @@
CircularBorderDrawable newCircularDrawable() {
return new CircularBorderDrawableLollipop();
}
+
+ void getPadding(Rect rect) {
+ if (mShadowViewDelegate.isCompatPaddingEnabled()) {
+ final float radius = mShadowViewDelegate.getRadius();
+ final float maxShadowSize = getElevation() + mPressedTranslationZ;
+ final int hPadding = (int) Math.ceil(
+ ShadowDrawableWrapper.calculateHorizontalPadding(maxShadowSize, radius, false));
+ final int vPadding = (int) Math.ceil(
+ ShadowDrawableWrapper.calculateVerticalPadding(maxShadowSize, radius, false));
+ rect.set(hPadding, vPadding, hPadding, vPadding);
+ } else {
+ rect.set(0, 0, 0, 0);
+ }
+ }
}
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index 3079180..29ca0af 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -34,6 +34,8 @@
<attr name="pressedTranslationZ" format="dimension"/>
<!-- The width of the border around the FAB. -->
<attr name="borderWidth" format="dimension"/>
+ <!-- Enable compat padding. -->
+ <attr name="useCompatPadding" format="boolean"/>
</declare-styleable>
<declare-styleable name="ScrimInsetsFrameLayout">
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index 894f5e3..fbb4b5f 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -92,6 +92,7 @@
private int mSize;
private int mImagePadding;
+ private boolean mCompatPadding;
private final Rect mShadowPadding;
private final FloatingActionButtonImpl mImpl;
@@ -123,6 +124,7 @@
final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f);
final float pressedTranslationZ = a.getDimension(
R.styleable.FloatingActionButton_pressedTranslationZ, 0f);
+ mCompatPadding = a.getBoolean(R.styleable.FloatingActionButton_useCompatPadding, false);
a.recycle();
final ShadowViewDelegate delegate = new ShadowViewDelegate() {
@@ -134,7 +136,6 @@
@Override
public void setShadowPadding(int left, int top, int right, int bottom) {
mShadowPadding.set(left, top, right, bottom);
-
setPadding(left + mImagePadding, top + mImagePadding,
right + mImagePadding, bottom + mImagePadding);
}
@@ -143,6 +144,11 @@
public void setBackgroundDrawable(Drawable background) {
FloatingActionButton.super.setBackgroundDrawable(background);
}
+
+ @Override
+ public boolean isCompatPaddingEnabled() {
+ return mCompatPadding;
+ }
};
final int sdk = Build.VERSION.SDK_INT;
@@ -161,6 +167,7 @@
mRippleColor, mBorderWidth);
mImpl.setElevation(elevation);
mImpl.setPressedTranslationZ(pressedTranslationZ);
+ mImpl.updatePadding();
}
@Override
@@ -186,6 +193,8 @@
* When running on devices with KitKat or below, we draw a fill rather than a ripple.
*
* @param color ARGB color to use for the ripple.
+ *
+ * @attr ref android.support.design.R.styleable#FloatingActionButton_rippleColor
*/
public void setRippleColor(@ColorInt int color) {
if (mRippleColor != color) {
@@ -219,7 +228,6 @@
}
}
-
/**
* Return the blending mode used to apply the tint to the background
* drawable, if specified.
@@ -308,6 +316,36 @@
mImpl.hide(wrapOnVisibilityChangedListener(listener), fromUser);
}
+ /**
+ * Set whether FloatingActionButton should add inner padding on platforms Lollipop and after,
+ * to ensure consistent dimensions on all platforms.
+ *
+ * @param useCompatPadding true if FloatingActionButton is adding inner padding on platforms
+ * Lollipop and after, to ensure consistent dimensions on all platforms.
+ *
+ * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding
+ * @see #getUseCompatPadding()
+ */
+ public void setUseCompatPadding(boolean useCompatPadding) {
+ if (mCompatPadding != useCompatPadding) {
+ mCompatPadding = useCompatPadding;
+ mImpl.onCompatShadowChanged();
+ }
+ }
+
+ /**
+ * Returns whether FloatingActionButton will add inner padding on platforms Lollipop and after.
+ *
+ * @return true if FloatingActionButton is adding inner padding on platforms Lollipop and after,
+ * to ensure consistent dimensions on all platforms.
+ *
+ * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding
+ * @see #setUseCompatPadding(boolean)
+ */
+ public boolean getUseCompatPadding() {
+ return mCompatPadding;
+ }
+
@Nullable
private InternalVisibilityChangedListener wrapOnVisibilityChangedListener(
@Nullable final OnVisibilityChangedListener listener) {
@@ -611,4 +649,27 @@
}
}
}
+
+ /**
+ * Returns the backward compatible elevation of the FloatingActionButton.
+ *
+ * @returns the backward compatible elevation in pixels.
+ * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation
+ * @see #setFloatingActionButtonElevation(float)
+ */
+ public float getFloatingActionButtonElevation() {
+ return mImpl.getElevation();
+ }
+
+ /**
+ * Updates the backward compatible elevation of the FloatingActionButton.
+ *
+ * @param elevation The backward compatible elevation in pixels.
+ * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation
+ * @see #getFloatingActionButtonElevation()
+ * @see #setUseCompatPadding(boolean)
+ */
+ public void setFloatingActionButtonElevation(float elevation) {
+ mImpl.setElevation(elevation);
+ }
}