Material Design Team cfc00aa5a3 Update android core class definitions across material
PiperOrigin-RevId: 414839941
2021-12-08 14:56:25 +00:00

190 lines
5.9 KiB
Java

/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.material.shadow;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Region.Op;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.ColorUtils;
/**
* A helper class to draw linear or radial shadows using gradient shaders.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
public class ShadowRenderer {
/** Gradient start color of 68 which evaluates to approximately 26% opacity. */
private static final int COLOR_ALPHA_START = 0x44;
/** Gradient start color of 20 which evaluates to approximately 8% opacity. */
private static final int COLOR_ALPHA_MIDDLE = 0x14;
private static final int COLOR_ALPHA_END = 0;
@NonNull private final Paint shadowPaint;
@NonNull private final Paint cornerShadowPaint;
@NonNull private final Paint edgeShadowPaint;
private int shadowStartColor;
private int shadowMiddleColor;
private int shadowEndColor;
private static final int[] edgeColors = new int[3];
/** Start, middle of shadow, and end of shadow positions */
private static final float[] edgePositions = new float[] {0f, .5f, 1f};
private static final int[] cornerColors = new int[4];
/** Start, beginning of corner, middle of shadow, and end of shadow positions */
private static final float[] cornerPositions = new float[] {0f, 0f, .5f, 1f};
private final Path scratch = new Path();
private Paint transparentPaint = new Paint();
public ShadowRenderer() {
this(Color.BLACK);
}
public ShadowRenderer(int color) {
shadowPaint = new Paint();
setShadowColor(color);
transparentPaint.setColor(Color.TRANSPARENT);
cornerShadowPaint = new Paint(Paint.DITHER_FLAG);
cornerShadowPaint.setStyle(Paint.Style.FILL);
edgeShadowPaint = new Paint(cornerShadowPaint);
}
public void setShadowColor(int color) {
shadowStartColor = ColorUtils.setAlphaComponent(color, COLOR_ALPHA_START);
shadowMiddleColor = ColorUtils.setAlphaComponent(color, COLOR_ALPHA_MIDDLE);
shadowEndColor = ColorUtils.setAlphaComponent(color, COLOR_ALPHA_END);
shadowPaint.setColor(shadowStartColor);
}
/** Draws an edge shadow on the canvas in the current bounds with the matrix transform applied. */
public void drawEdgeShadow(
@NonNull Canvas canvas, @Nullable Matrix transform, @NonNull RectF bounds, int elevation) {
bounds.bottom += elevation;
bounds.offset(0, -elevation);
edgeColors[0] = shadowEndColor;
edgeColors[1] = shadowMiddleColor;
edgeColors[2] = shadowStartColor;
edgeShadowPaint.setShader(
new LinearGradient(
bounds.left,
bounds.top,
bounds.left,
bounds.bottom,
edgeColors,
edgePositions,
Shader.TileMode.CLAMP));
canvas.save();
canvas.concat(transform);
canvas.drawRect(bounds, edgeShadowPaint);
canvas.restore();
}
/**
* Draws a corner shadow on the canvas in the current bounds with the matrix transform applied.
*/
public void drawCornerShadow(
@NonNull Canvas canvas,
@Nullable Matrix matrix,
@NonNull RectF bounds,
int elevation,
float startAngle,
float sweepAngle) {
boolean drawShadowInsideBounds = sweepAngle < 0;
Path arcBounds = scratch;
if (drawShadowInsideBounds) {
cornerColors[0] = 0;
cornerColors[1] = shadowEndColor;
cornerColors[2] = shadowMiddleColor;
cornerColors[3] = shadowStartColor;
} else {
// Calculate the arc bounds to prevent drawing shadow in the same part of the arc.
arcBounds.rewind();
arcBounds.moveTo(bounds.centerX(), bounds.centerY());
arcBounds.arcTo(bounds, startAngle, sweepAngle);
arcBounds.close();
bounds.inset(-elevation, -elevation);
cornerColors[0] = 0;
cornerColors[1] = shadowStartColor;
cornerColors[2] = shadowMiddleColor;
cornerColors[3] = shadowEndColor;
}
float radius = bounds.width() / 2f;
// The shadow is not big enough to draw.
if (radius <= 0) {
return;
}
float startRatio = 1f - (elevation / radius);
float midRatio = startRatio + ((1f - startRatio) / 2f);
cornerPositions[1] = startRatio;
cornerPositions[2] = midRatio;
RadialGradient shader = new RadialGradient(
bounds.centerX(),
bounds.centerY(),
radius,
cornerColors,
cornerPositions,
TileMode.CLAMP);
cornerShadowPaint.setShader(shader);
canvas.save();
canvas.concat(matrix);
canvas.scale(1, bounds.height() / bounds.width());
if (!drawShadowInsideBounds) {
canvas.clipPath(arcBounds, Op.DIFFERENCE);
// This line is required for the next drawArc to work correctly, I think.
canvas.drawPath(arcBounds, transparentPaint);
}
canvas.drawArc(bounds, startAngle, sweepAngle, true, cornerShadowPaint);
canvas.restore();
}
@NonNull
public Paint getShadowPaint() {
return shadowPaint;
}
}