FloatActionButton用来做阴影的主要是ShadowDrawableWrapper,这个功能相对独立,可以直接把这个类和他继承的DrawableWrapper 拷出来放在自己的包下面,然后重新导包就行了。
这个图上面的三个颜色,我们可以自定义,也就是阴影的渐变三色。
源码:
public class ShadowDrawableWrapper extends DrawableWrapper {
static final double COS_45 = Math.cos(Math.toRadians(45.0D));
static final float SHADOW_MULTIPLIER = 1.5F;
static final float SHADOW_TOP_SCALE = 0.25F;
static final float SHADOW_HORIZ_SCALE = 0.5F;
static final float SHADOW_BOTTOM_SCALE = 1.0F;
final Paint cornerShadowPaint;
final Paint edgeShadowPaint;
final RectF contentBounds;
float cornerRadius;
Path cornerShadowPath;
float maxShadowSize;
float rawMaxShadowSize;
float shadowSize;
float rawShadowSize;
private boolean dirty = true;
private final int shadowStartColor;
private final int shadowMiddleColor;
private final int shadowEndColor;
private boolean addPaddingForCorners = true;
private float rotation;
private boolean printedShadowClipWarning = false;
public ShadowDrawableWrapper(Context context, Drawable content, float radius, float shadowSize, float maxShadowSize) {
super(content);
this.shadowStartColor = ContextCompat.getColor(context, R.color.design_fab_shadow_start_color);
this.shadowMiddleColor = ContextCompat.getColor(context, R.color.design_fab_shadow_mid_color);
this.shadowEndColor = ContextCompat.getColor(context, R.color.design_fab_shadow_end_color);
this.cornerShadowPaint = new Paint(5);
this.cornerShadowPaint.setStyle(Paint.Style.FILL);
this.cornerRadius = (float)Math.round(radius);
this.contentBounds = new RectF();
this.edgeShadowPaint = new Paint(this.cornerShadowPaint);
this.edgeShadowPaint.setAntiAlias(false);
this.setShadowSize(shadowSize, maxShadowSize);
}
private static int toEven(float value) {
int i = Math.round(value);
return i % 2 == 1 ? i - 1 : i;
}
public void setAddPaddingForCorners(boolean addPaddingForCorners) {
this.addPaddingForCorners = addPaddingForCorners;
this.invalidateSelf();
}
public void setAlpha(int alpha) {
super.setAlpha(alpha);
this.cornerShadowPaint.setAlpha(alpha);
this.edgeShadowPaint.setAlpha(alpha);
}
protected void onBoundsChange(Rect bounds) {
this.dirty = true;
}
public void setShadowSize(float shadowSize, float maxShadowSize) {
if (shadowSize >= 0.0F && maxShadowSize >= 0.0F) {
shadowSize = (float)toEven(shadowSize);
maxShadowSize = (float)toEven(maxShadowSize);
if (shadowSize > maxShadowSize) {
shadowSize = maxShadowSize;
if (!this.printedShadowClipWarning) {
this.printedShadowClipWarning = true;
}
}
if (this.rawShadowSize != shadowSize || this.rawMaxShadowSize != maxShadowSize) {
this.rawShadowSize = shadowSize;
this.rawMaxShadowSize = maxShadowSize;
this.shadowSize = (float)Math.round(shadowSize * 1.5F);
this.maxShadowSize = maxShadowSize;
this.dirty = true;
this.invalidateSelf();
}
} else {
throw new IllegalArgumentException("invalid shadow size");
}
}
public void setShadowSize(float size) {
this.setShadowSize(size, this.rawMaxShadowSize);
}
public float getShadowSize() {
return this.rawShadowSize;
}
public boolean getPadding(Rect padding) {
int vOffset = (int)Math.ceil((double)calculateVerticalPadding(this.rawMaxShadowSize, this.cornerRadius, this.addPaddingForCorners));
int hOffset = (int)Math.ceil((double)calculateHorizontalPadding(this.rawMaxShadowSize, this.cornerRadius, this.addPaddingForCorners));
padding.set(hOffset, vOffset, hOffset, vOffset);
return true;
}
public static float calculateVerticalPadding(float maxShadowSize, float cornerRadius, boolean addPaddingForCorners) {
return addPaddingForCorners ? (float)((double)(maxShadowSize * 1.5F) + (1.0D - COS_45) * (double)cornerRadius) : maxShadowSize * 1.5F;
}
public static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius, boolean addPaddingForCorners) {
return addPaddingForCorners ? (float)((double)maxShadowSize + (1.0D - COS_45) * (double)cornerRadius) : maxShadowSize;
}
public int getOpacity() {
return -3;
}
public void setCornerRadius(float radius) {
radius = (float)Math.round(radius);
if (this.cornerRadius != radius) {
this.cornerRadius = radius;
this.dirty = true;
this.invalidateSelf();
}
}
public void draw(Canvas canvas) {
if (this.dirty) {
this.buildComponents(this.getBounds());
this.dirty = false;
}
this.drawShadow(canvas);
super.draw(canvas);
}
public final void setRotation(float rotation) {
if (this.rotation != rotation) {
this.rotation = rotation;
this.invalidateSelf();
}
}
private void drawShadow(Canvas canvas) {
int rotateSaved = canvas.save();
canvas.rotate(this.rotation, this.contentBounds.centerX(), this.contentBounds.centerY());
float edgeShadowTop = -this.cornerRadius - this.shadowSize;
float shadowOffset = this.cornerRadius;
boolean drawHorizontalEdges = this.contentBounds.width() - 2.0F * shadowOffset > 0.0F;
boolean drawVerticalEdges = this.contentBounds.height() - 2.0F * shadowOffset > 0.0F;
float shadowOffsetTop = this.rawShadowSize - this.rawShadowSize * 0.25F;
float shadowOffsetHorizontal = this.rawShadowSize - this.rawShadowSize * 0.5F;
float shadowOffsetBottom = this.rawShadowSize - this.rawShadowSize * 1.0F;
float shadowScaleHorizontal = shadowOffset / (shadowOffset + shadowOffsetHorizontal);
float shadowScaleTop = shadowOffset / (shadowOffset + shadowOffsetTop);
float shadowScaleBottom = shadowOffset / (shadowOffset + shadowOffsetBottom);
int saved = canvas.save();
canvas.translate(this.contentBounds.left + shadowOffset, this.contentBounds.top + shadowOffset);
canvas.scale(shadowScaleHorizontal, shadowScaleTop);
canvas.drawPath(this.cornerShadowPath, this.cornerShadowPaint);
if (drawHorizontalEdges) {
canvas.scale(1.0F / shadowScaleHorizontal, 1.0F);
canvas.drawRect(0.0F, edgeShadowTop, this.contentBounds.width() - 2.0F * shadowOffset, -this.cornerRadius, this.edgeShadowPaint);
}
canvas.restoreToCount(saved);
saved = canvas.save();
canvas.translate(this.contentBounds.right - shadowOffset, this.contentBounds.bottom - shadowOffset);
canvas.scale(shadowScaleHorizontal, shadowScaleBottom);
canvas.rotate(180.0F);
canvas.drawPath(this.cornerShadowPath, this.cornerShadowPaint);
if (drawHorizontalEdges) {
canvas.scale(1.0F / shadowScaleHorizontal, 1.0F);
canvas.drawRect(0.0F, edgeShadowTop, this.contentBounds.width() - 2.0F * shadowOffset, -this.cornerRadius + this.shadowSize, this.edgeShadowPaint);
}
canvas.restoreToCount(saved);
saved = canvas.save();
canvas.translate(this.contentBounds.left + shadowOffset, this.contentBounds.bottom - shadowOffset);
canvas.scale(shadowScaleHorizontal, shadowScaleBottom);
canvas.rotate(270.0F);
canvas.drawPath(this.cornerShadowPath, this.cornerShadowPaint);
if (drawVerticalEdges) {
canvas.scale(1.0F / shadowScaleBottom, 1.0F);
canvas.drawRect(0.0F, edgeShadowTop, this.contentBounds.height() - 2.0F * shadowOffset, -this.cornerRadius, this.edgeShadowPaint);
}
canvas.restoreToCount(saved);
saved = canvas.save();
canvas.translate(this.contentBounds.right - shadowOffset, this.contentBounds.top + shadowOffset);
canvas.scale(shadowScaleHorizontal, shadowScaleTop);
canvas.rotate(90.0F);
canvas.drawPath(this.cornerShadowPath, this.cornerShadowPaint);
if (drawVerticalEdges) {
canvas.scale(1.0F / shadowScaleTop, 1.0F);
canvas.drawRect(0.0F, edgeShadowTop, this.contentBounds.height() - 2.0F * shadowOffset, -this.cornerRadius, this.edgeShadowPaint);
}
canvas.restoreToCount(saved);
canvas.restoreToCount(rotateSaved);
}
private void buildShadowCorners() {
RectF innerBounds = new RectF(-this.cornerRadius, -this.cornerRadius, this.cornerRadius, this.cornerRadius);
RectF outerBounds = new RectF(innerBounds);
outerBounds.inset(-this.shadowSize, -this.shadowSize);
if (this.cornerShadowPath == null) {
this.cornerShadowPath = new Path();
} else {
this.cornerShadowPath.reset();
}
this.cornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
this.cornerShadowPath.moveTo(-this.cornerRadius, 0.0F);
this.cornerShadowPath.rLineTo(-this.shadowSize, 0.0F);
this.cornerShadowPath.arcTo(outerBounds, 180.0F, 90.0F, false);
this.cornerShadowPath.arcTo(innerBounds, 270.0F, -90.0F, false);
this.cornerShadowPath.close();
float shadowRadius = -outerBounds.top;
if (shadowRadius > 0.0F) {
float startRatio = this.cornerRadius / shadowRadius;
float midRatio = startRatio + (1.0F - startRatio) / 2.0F;
this.cornerShadowPaint.setShader(new RadialGradient(0.0F, 0.0F, shadowRadius, new int[]{0, this.shadowStartColor, this.shadowMiddleColor, this.shadowEndColor}, new float[]{0.0F, startRatio, midRatio, 1.0F}, Shader.TileMode.CLAMP));
}
this.edgeShadowPaint.setShader(new LinearGradient(0.0F, innerBounds.top, 0.0F, outerBounds.top, new int[]{this.shadowStartColor, this.shadowMiddleColor, this.shadowEndColor}, new float[]{0.0F, 0.5F, 1.0F}, Shader.TileMode.CLAMP));
this.edgeShadowPaint.setAntiAlias(false);
}
private void buildComponents(Rect bounds) {
float verticalOffset = this.rawMaxShadowSize * 1.5F;
this.contentBounds.set((float)bounds.left + this.rawMaxShadowSize, (float)bounds.top + verticalOffset, (float)bounds.right - this.rawMaxShadowSize, (float)bounds.bottom - verticalOffset);
this.getWrappedDrawable().setBounds((int)this.contentBounds.left, (int)this.contentBounds.top, (int)this.contentBounds.right, (int)this.contentBounds.bottom);
this.buildShadowCorners();
}
public float getCornerRadius() {
return this.cornerRadius;
}
public void setMaxShadowSize(float size) {
this.setShadowSize(this.rawShadowSize, size);
}
public float getMaxShadowSize() {
return this.rawMaxShadowSize;
}
public float getMinWidth() {
float content = 2.0F * Math.max(this.rawMaxShadowSize, this.cornerRadius + this.rawMaxShadowSize / 2.0F);
return content + this.rawMaxShadowSize * 2.0F;
}
public float getMinHeight() {
float content = 2.0F * Math.max(this.rawMaxShadowSize, this.cornerRadius + this.rawMaxShadowSize * 1.5F / 2.0F);
return content + this.rawMaxShadowSize * 1.5F * 2.0F;
}
}
DrawableWrapper源码:
@RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
public class DrawableWrapper extends Drawable implements Drawable.Callback {
private Drawable mDrawable;
public DrawableWrapper(Drawable drawable) {
this.setWrappedDrawable(drawable);
}
public void draw(Canvas canvas) {
this.mDrawable.draw(canvas);
}
protected void onBoundsChange(Rect bounds) {
this.mDrawable.setBounds(bounds);
}
public void setChangingConfigurations(int configs) {
this.mDrawable.setChangingConfigurations(configs);
}
public int getChangingConfigurations() {
return this.mDrawable.getChangingConfigurations();
}
public void setDither(boolean dither) {
this.mDrawable.setDither(dither);
}
public void setFilterBitmap(boolean filter) {
this.mDrawable.setFilterBitmap(filter);
}
public void setAlpha(int alpha) {
this.mDrawable.setAlpha(alpha);
}
public void setColorFilter(ColorFilter cf) {
this.mDrawable.setColorFilter(cf);
}
public boolean isStateful() {
return this.mDrawable.isStateful();
}
public boolean setState(int[] stateSet) {
return this.mDrawable.setState(stateSet);
}
public int[] getState() {
return this.mDrawable.getState();
}
public void jumpToCurrentState() {
DrawableCompat.jumpToCurrentState(this.mDrawable);
}
public Drawable getCurrent() {
return this.mDrawable.getCurrent();
}
public boolean setVisible(boolean visible, boolean restart) {
return super.setVisible(visible, restart) || this.mDrawable.setVisible(visible, restart);
}
public int getOpacity() {
return this.mDrawable.getOpacity();
}
public Region getTransparentRegion() {
return this.mDrawable.getTransparentRegion();
}
public int getIntrinsicWidth() {
return this.mDrawable.getIntrinsicWidth();
}
public int getIntrinsicHeight() {
return this.mDrawable.getIntrinsicHeight();
}
public int getMinimumWidth() {
return this.mDrawable.getMinimumWidth();
}
public int getMinimumHeight() {
return this.mDrawable.getMinimumHeight();
}
public boolean getPadding(Rect padding) {
return this.mDrawable.getPadding(padding);
}
public void invalidateDrawable(Drawable who) {
this.invalidateSelf();
}
public void scheduleDrawable(Drawable who, Runnable what, long when) {
this.scheduleSelf(what, when);
}
public void unscheduleDrawable(Drawable who, Runnable what) {
this.unscheduleSelf(what);
}
protected boolean onLevelChange(int level) {
return this.mDrawable.setLevel(level);
}
public void setAutoMirrored(boolean mirrored) {
DrawableCompat.setAutoMirrored(this.mDrawable, mirrored);
}
public boolean isAutoMirrored() {
return DrawableCompat.isAutoMirrored(this.mDrawable);
}
public void setTint(int tint) {
DrawableCompat.setTint(this.mDrawable, tint);
}
public void setTintList(ColorStateList tint) {
DrawableCompat.setTintList(this.mDrawable, tint);
}
public void setTintMode(PorterDuff.Mode tintMode) {
DrawableCompat.setTintMode(this.mDrawable, tintMode);
}
public void setHotspot(float x, float y) {
DrawableCompat.setHotspot(this.mDrawable, x, y);
}
public void setHotspotBounds(int left, int top, int right, int bottom) {
DrawableCompat.setHotspotBounds(this.mDrawable, left, top, right, bottom);
}
public Drawable getWrappedDrawable() {
return this.mDrawable;
}
public void setWrappedDrawable(Drawable drawable) {
if (this.mDrawable != null) {
this.mDrawable.setCallback((Callback)null);
}
this.mDrawable = drawable;
if (drawable != null) {
drawable.setCallback(this);
}
}
}