Skip to content

Commit 21ba18a

Browse files
pekingmeimhappi
authored andcommitted
[Shape] Added Material endorsed shapes.
PiperOrigin-RevId: 663370897
1 parent f3aacd7 commit 21ba18a

File tree

4 files changed

+974
-0
lines changed

4 files changed

+974
-0
lines changed

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ ext {
4141
vectorDrawable : '1.1.0',
4242
viewpager2 : '1.0.0',
4343
dynamicanimation : '1.0.0',
44+
graphicsShapes : '1.0.0-rc01',
4445
]
4546

4647
errorproneVersion = '2.15.0'
@@ -123,6 +124,8 @@ def compatibility(name) {
123124
return "androidx.resourceinspection:resourceinspection-processor:${androidXVersions.resourceInspectionProcessor}"
124125
case "viewpager2":
125126
return "androidx.viewpager2:viewpager2:${androidXVersions.viewpager2}"
127+
case "graphicsShapes":
128+
return "androidx.graphics:graphics-shapes:${androidXVersions.graphicsShapes}"
126129
case "experimental":
127130
return "androidx.annotation:annotation-experimental:${androidXVersions.experimental}"
128131
default:

lib/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies {
2323
api compatibility("transition")
2424
api compatibility("vectordrawable")
2525
api compatibility("viewpager2")
26+
api compatibility("graphicsShapes")
2627

2728
annotationProcessor compatibility("resourceInspectionProcessor")
2829

@@ -66,6 +67,7 @@ def srcDirs = [
6667
'com/google/android/material/floatingactionbutton',
6768
'com/google/android/material/imageview',
6869
'com/google/android/material/internal',
70+
'com/google/android/material/loadingindicator',
6971
'com/google/android/material/materialswitch',
7072
'com/google/android/material/math',
7173
'com/google/android/material/menu',

lib/java/com/google/android/material/shape/MaterialShapeUtils.java

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,19 @@
1616

1717
package com.google.android.material.shape;
1818

19+
import static java.lang.Math.min;
20+
21+
import android.graphics.Matrix;
22+
import android.graphics.RectF;
1923
import android.graphics.drawable.Drawable;
24+
import android.graphics.drawable.ShapeDrawable;
25+
import android.graphics.drawable.shapes.PathShape;
2026
import android.view.View;
2127
import androidx.annotation.NonNull;
28+
import androidx.annotation.RestrictTo;
29+
import androidx.annotation.RestrictTo.Scope;
30+
import androidx.graphics.shapes.RoundedPolygon;
31+
import androidx.graphics.shapes.Shapes_androidKt;
2232
import com.google.android.material.internal.ViewUtils;
2333

2434
/** Utility methods for {@link MaterialShapeDrawable} and related classes. */
@@ -84,4 +94,127 @@ public static void setParentAbsoluteElevation(
8494
materialShapeDrawable.setParentAbsoluteElevation(ViewUtils.getParentAbsoluteElevation(view));
8595
}
8696
}
97+
98+
/**
99+
* Returns a {@link ShapeDrawable} with the shape's path.
100+
*
101+
* <p>The shape is always assumed to fit in (0, 0) to (1, 1) square.
102+
*
103+
* @param shape A {@link RoundedPolygon} object to be used in the drawable.
104+
* @hide
105+
*/
106+
@NonNull
107+
@RestrictTo(Scope.LIBRARY_GROUP)
108+
public static ShapeDrawable createShapeDrawable(@NonNull RoundedPolygon shape) {
109+
PathShape pathShape = new PathShape(Shapes_androidKt.toPath(shape), 1, 1);
110+
return new ShapeDrawable(pathShape);
111+
}
112+
113+
/**
114+
* Creates a new {@link RoundedPolygon}, moving and resizing this one, so it's completely inside
115+
* the destination bounds.
116+
*
117+
* <p>If {@code radial} is true, the shape will be scaled to fit in the biggest circle centered in
118+
* the destination bounds. This is useful when the shape is animated to rotate around its center.
119+
* Otherwise, the shape will be scaled to fit in the destination bounds. With either option, the
120+
* shape's original center will be aligned with the destination bounds center.
121+
*
122+
* @param shape The original {@link RoundedPolygon}.
123+
* @param radial Whether to transform the shape to fit in the biggest circle centered in the
124+
* destination bounds.
125+
* @param dstBounds The destination bounds to fit.
126+
* @return A new {@link RoundedPolygon} that fits in the destination bounds.
127+
* @hide
128+
*/
129+
@NonNull
130+
@RestrictTo(Scope.LIBRARY_GROUP)
131+
public static RoundedPolygon normalize(
132+
@NonNull RoundedPolygon shape, boolean radial, @NonNull RectF dstBounds) {
133+
float[] srcBoundsArray = new float[4];
134+
if (radial) {
135+
// This calculates the axis-aligned bounds of the shape and returns that rectangle. It
136+
// determines the max dimension of the shape (by calculating the distance from its center to
137+
// the start and midpoint of each curve) and returns a square which can be used to hold the
138+
// object in any rotation.
139+
shape.calculateMaxBounds(srcBoundsArray);
140+
} else {
141+
// This calculates the bounds of the shape without rotating the shape.
142+
shape.calculateBounds(srcBoundsArray);
143+
}
144+
RectF srcBounds =
145+
new RectF(srcBoundsArray[0], srcBoundsArray[1], srcBoundsArray[2], srcBoundsArray[3]);
146+
float scale =
147+
min(dstBounds.width() / srcBounds.width(), dstBounds.height() / srcBounds.height());
148+
// Scales the shape with pivot point at its original center then moves it to align its original
149+
// center with the destination bounds center.
150+
Matrix transform = createScaleMatrix(scale, scale);
151+
transform.preTranslate(-srcBounds.centerX(), -srcBounds.centerY());
152+
transform.postTranslate(dstBounds.centerX(), dstBounds.centerY());
153+
return Shapes_androidKt.transformed(shape, transform);
154+
}
155+
156+
/**
157+
* Creates a new {@link RoundedPolygon}, moving and resizing this one, so it's completely inside
158+
* (0, 0) - (1, 1) square.
159+
*
160+
* <p>If {@code radial} is true, the shape will be scaled to fit in the circle centered at (0.5,
161+
* 0.5) with a radius of 0.5. This is useful when the shape is animated to rotate around its
162+
* center. Otherwise, the shape will be scaled to fit in the (0, 0) - (1, 1) square. With either
163+
* option, the shape center will be (0.5, 0.5).
164+
*
165+
* @param shape The original {@link RoundedPolygon}.
166+
* @param radial Whether to transform the shape to fit in the circle centered at (0.5, 0.5) with a
167+
* radius of 0.5.
168+
* @return A new {@link RoundedPolygon} that fits in (0, 0) - (1, 1) square.
169+
* @hide
170+
*/
171+
@NonNull
172+
@RestrictTo(Scope.LIBRARY_GROUP)
173+
public static RoundedPolygon normalize(@NonNull RoundedPolygon shape, boolean radial) {
174+
return normalize(shape, radial, new RectF(0, 0, 1, 1));
175+
}
176+
177+
/**
178+
* Returns a {@link Matrix} with the input scales.
179+
*
180+
* @param scaleX Scale in X axis.
181+
* @param scaleY Scale in Y axis
182+
* @hide
183+
*/
184+
@NonNull
185+
@RestrictTo(Scope.LIBRARY_GROUP)
186+
static Matrix createScaleMatrix(float scaleX, float scaleY) {
187+
Matrix matrix = new Matrix();
188+
matrix.setScale(scaleX, scaleY);
189+
return matrix;
190+
}
191+
192+
/**
193+
* Returns a {@link Matrix} with the input rotation in degrees.
194+
*
195+
* @param degrees The rotation in degrees.
196+
* @hide
197+
*/
198+
@NonNull
199+
@RestrictTo(Scope.LIBRARY_GROUP)
200+
static Matrix createRotationMatrix(float degrees) {
201+
Matrix matrix = new Matrix();
202+
matrix.setRotate(degrees);
203+
return matrix;
204+
}
205+
206+
/**
207+
* Returns a {@link Matrix} with the input skews.
208+
*
209+
* @param kx The skew in X axis.
210+
* @param ky The skew in Y axis.
211+
* @hide
212+
*/
213+
@NonNull
214+
@RestrictTo(Scope.LIBRARY_GROUP)
215+
static Matrix createSkewMatrix(float kx, float ky) {
216+
Matrix matrix = new Matrix();
217+
matrix.setSkew(kx, ky);
218+
return matrix;
219+
}
87220
}

0 commit comments

Comments
 (0)