Skip to content

Commit 98169d2

Browse files
committed
[Motion] Added spring motion subsystem
PiperOrigin-RevId: 705572242
1 parent 9397801 commit 98169d2

File tree

10 files changed

+236
-15
lines changed

10 files changed

+236
-15
lines changed

lib/java/com/google/android/material/button/MaterialButton.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import androidx.dynamicanimation.animation.SpringForce;
7575
import com.google.android.material.internal.ThemeEnforcement;
7676
import com.google.android.material.internal.ViewUtils;
77+
import com.google.android.material.motion.MotionUtils;
7778
import androidx.resourceinspection.annotation.Attribute;
7879
import com.google.android.material.resources.MaterialResources;
7980
import com.google.android.material.shape.MaterialShapeUtils;
@@ -216,10 +217,6 @@ interface OnPressedChangeListener {
216217

217218
@AttrRes private static final int MATERIAL_SIZE_OVERLAY_ATTR = R.attr.materialSizeOverlay;
218219

219-
// Use Fast Bouncy spring as default.
220-
private static final float DEFAULT_BUTTON_SPRING_DAMPING = 0.6f;
221-
private static final float DEFAULT_BUTTON_SPRING_STIFFNESS = 800f;
222-
223220
private static final int UNSET = -1;
224221

225222
@NonNull private final MaterialButtonHelper materialButtonHelper;
@@ -317,9 +314,7 @@ private void initializeSizeAnimation() {
317314
}
318315

319316
private SpringForce createSpringForce() {
320-
return new SpringForce()
321-
.setDampingRatio(DEFAULT_BUTTON_SPRING_DAMPING)
322-
.setStiffness(DEFAULT_BUTTON_SPRING_STIFFNESS);
317+
return MotionUtils.resolveThemeSpringForce(getContext(), R.attr.motionSpringFastSpacial);
323318
}
324319

325320
@NonNull

lib/java/com/google/android/material/dialog/res/values/themes_base.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,12 @@
257257
<item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.Material3.LargeComponent</item>
258258

259259
<!-- Motion attributes. -->
260+
<item name="motionSpringFastSpacial">@style/Motion.Material3.Spring.Standard.Fast.Spacial</item>
261+
<item name="motionSpringFastEffects">@style/Motion.Material3.Spring.Standard.Fast.Effects</item>
262+
<item name="motionSpringDefaultSpacial">@style/Motion.Material3.Spring.Standard.Default.Spacial</item>
263+
<item name="motionSpringDefaultEffects">@style/Motion.Material3.Spring.Standard.Default.Effects</item>
264+
<item name="motionSpringSlowSpacial">@style/Motion.Material3.Spring.Standard.Slow.Spacial</item>
265+
<item name="motionSpringSlowEffects">@style/Motion.Material3.Spring.Standard.Slow.Effects</item>
260266
<item name="motionEasingStandardInterpolator">@interpolator/m3_sys_motion_easing_standard</item>
261267
<item name="motionEasingStandardDecelerateInterpolator">@interpolator/m3_sys_motion_easing_standard_decelerate</item>
262268
<item name="motionEasingStandardAccelerateInterpolator">@interpolator/m3_sys_motion_easing_standard_accelerate</item>
@@ -544,6 +550,12 @@
544550
<item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.Material3.LargeComponent</item>
545551

546552
<!-- Motion attributes. -->
553+
<item name="motionSpringFastSpacial">@style/Motion.Material3.Spring.Standard.Fast.Spacial</item>
554+
<item name="motionSpringFastEffects">@style/Motion.Material3.Spring.Standard.Fast.Effects</item>
555+
<item name="motionSpringDefaultSpacial">@style/Motion.Material3.Spring.Standard.Default.Spacial</item>
556+
<item name="motionSpringDefaultEffects">@style/Motion.Material3.Spring.Standard.Default.Effects</item>
557+
<item name="motionSpringSlowSpacial">@style/Motion.Material3.Spring.Standard.Slow.Spacial</item>
558+
<item name="motionSpringSlowEffects">@style/Motion.Material3.Spring.Standard.Slow.Effects</item>
547559
<item name="motionEasingStandardInterpolator">@interpolator/m3_sys_motion_easing_standard</item>
548560
<item name="motionEasingStandardDecelerateInterpolator">@interpolator/m3_sys_motion_easing_standard_decelerate</item>
549561
<item name="motionEasingStandardAccelerateInterpolator">@interpolator/m3_sys_motion_easing_standard_accelerate</item>

lib/java/com/google/android/material/motion/MotionUtils.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@
1515
*/
1616
package com.google.android.material.motion;
1717

18+
import com.google.android.material.R;
19+
1820
import android.animation.TimeInterpolator;
1921
import android.content.Context;
22+
import android.content.res.TypedArray;
2023
import android.util.TypedValue;
2124
import android.view.animation.AnimationUtils;
2225
import androidx.annotation.AttrRes;
2326
import androidx.annotation.NonNull;
2427
import androidx.core.graphics.PathParser;
2528
import androidx.core.view.animation.PathInterpolatorCompat;
29+
import androidx.dynamicanimation.animation.SpringForce;
2630
import com.google.android.material.resources.MaterialAttributes;
2731

2832
/** A utility class for motion system functions. */
@@ -36,6 +40,40 @@ public class MotionUtils {
3640

3741
private MotionUtils() {}
3842

43+
/**
44+
* Resolve a {@link SpringForce} object from a Material spring theme attribute.
45+
*
46+
* @param context the context from where the theme attribute will be resolved
47+
* @param attrResId the {@code motionSpring*} theme attribute to resolve
48+
* into a {@link SpringForce} object
49+
* @return a {@link SpringForce} object configured using the stiffness and damping from the
50+
* resolved Material spring attribute
51+
*/
52+
@NonNull
53+
public static SpringForce resolveThemeSpringForce(
54+
@NonNull Context context, @AttrRes int attrResId) {
55+
TypedValue tv = MaterialAttributes.resolveTypedValueOrThrow(
56+
context, attrResId, "MaterialSpring");
57+
TypedArray a = context.obtainStyledAttributes(tv.resourceId, R.styleable.MaterialSpring);
58+
SpringForce springForce = new SpringForce();
59+
try {
60+
float stiffness = a.getFloat(R.styleable.MaterialSpring_stiffness, Float.MIN_VALUE);
61+
if (stiffness == Float.MIN_VALUE) {
62+
throw new IllegalArgumentException("A MaterialSpring style must have stiffness value.");
63+
}
64+
float damping = a.getFloat(R.styleable.MaterialSpring_damping, Float.MIN_VALUE);
65+
if (damping == Float.MIN_VALUE) {
66+
throw new IllegalArgumentException("A MaterialSpring style must have a damping value.");
67+
}
68+
69+
springForce.setStiffness(stiffness);
70+
springForce.setDampingRatio(damping);
71+
} finally {
72+
a.recycle();
73+
}
74+
return springForce;
75+
}
76+
3977
/**
4078
* Resolve a duration from a material duration theme attribute.
4179
*

lib/java/com/google/android/material/motion/res-public/values/public.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@
1515
~ limitations under the License.
1616
-->
1717
<resources>
18+
<public name="stiffness" type="attr"/>
19+
<public name="damping" type="attr"/>
1820

21+
<public name="motionSpringFastSpacial" type="attr" />
22+
<public name="motionSpringFastEffects" type="attr" />
23+
<public name="motionSpringDefaultSpacial" type="attr" />
24+
<public name="motionSpringDefaultEffects" type="attr" />
25+
<public name="motionSpringSlowSpacial" type="attr" />
26+
<public name="motionSpringSlowEffects" type="attr" />
1927
<public name="motionEasingStandardInterpolator" type="attr" />
2028
<public name="motionEasingStandardDecelerateInterpolator" type="attr" />
2129
<public name="motionEasingStandardAccelerateInterpolator" type="attr" />

lib/java/com/google/android/material/motion/res/values/attrs.xml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<?xml version="1.0" encoding="utf-8"?><!--
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
23
~ Copyright (C) 2022 The Android Open Source Project
34
~
45
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +16,34 @@
1516
-->
1617
<resources>
1718

19+
<declare-styleable name="MaterialSpring">
20+
<!-- Float used as the stiffness of a spring animation. The value must
21+
be greater than 0.-->
22+
<attr name="stiffness" format="float"/>
23+
<!-- Float used as the damping ratio of a spring animation. The value
24+
must be greater than or equal to 0 and is typically
25+
between 0 and 1, inclusive. -->
26+
<attr name="damping" format="float"/>
27+
</declare-styleable>
28+
29+
<!-- Spring for small components animations such as switches and buttons. -->
30+
<attr name="motionSpringFastSpacial" format="reference" />
31+
<!-- Spring for color or opacity change on small components such as a
32+
switch's thumb changing color. -->
33+
<attr name="motionSpringFastEffects" format="reference" />
34+
<!-- Spring for animations that partially cover the screen such as a bottom
35+
sheet sliding or a navigation rail expanding. -->
36+
<attr name="motionSpringDefaultSpacial" format="reference" />
37+
<!-- Spring for color or opacity changes of animations that partially cover
38+
the screen such as the opacity of the navigation rail content as the
39+
rail expands. -->
40+
<attr name="motionSpringDefaultEffects" format="reference" />
41+
<!-- Spring for full screen movements. -->
42+
<attr name="motionSpringSlowSpacial" format="reference" />
43+
<!-- Spring for full screen color or opacity changes such as content
44+
refreshes. -->
45+
<attr name="motionSpringSlowEffects" format="reference" />
46+
1847
<!-- Standard easing begins and ends at rest. It speeds up quickly and slows
1948
down gradually. This easing is used on elements that remain persistent
2049
during a transition. -->
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2024 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<resources>
18+
<style name="Motion.Material3.Spring.Standard.Fast.Spacial" parent="">
19+
<item name="stiffness">@dimen/m3_sys_motion_standard_spring_fast_spatial_stiffness</item>
20+
<item name="damping">@dimen/m3_sys_motion_standard_spring_fast_spatial_damping</item>
21+
</style>
22+
<style name="Motion.Material3.Spring.Standard.Default.Spacial" parent="">
23+
<item name="stiffness">@dimen/m3_sys_motion_standard_spring_default_spatial_stiffness</item>
24+
<item name="damping">@dimen/m3_sys_motion_standard_spring_default_spatial_damping</item>
25+
</style>
26+
<style name="Motion.Material3.Spring.Standard.Slow.Spacial" parent="">
27+
<item name="stiffness">@dimen/m3_sys_motion_standard_spring_slow_spatial_stiffness</item>
28+
<item name="damping">@dimen/m3_sys_motion_standard_spring_slow_spatial_damping</item>
29+
</style>
30+
<style name="Motion.Material3.Spring.Standard.Fast.Effects" parent="">
31+
<item name="stiffness">@dimen/m3_sys_motion_standard_spring_fast_effects_stiffness</item>
32+
<item name="damping">@dimen/m3_sys_motion_standard_spring_fast_effects_damping</item>
33+
</style>
34+
<style name="Motion.Material3.Spring.Standard.Default.Effects" parent="">
35+
<item name="stiffness">@dimen/m3_sys_motion_standard_spring_default_effects_stiffness</item>
36+
<item name="damping">@dimen/m3_sys_motion_standard_spring_default_effects_damping</item>
37+
</style>
38+
<style name="Motion.Material3.Spring.Standard.Slow.Effects" parent="">
39+
<item name="stiffness">@dimen/m3_sys_motion_standard_spring_slow_effects_stiffness</item>
40+
<item name="damping">@dimen/m3_sys_motion_standard_spring_slow_effects_damping</item>
41+
</style>
42+
</resources>

lib/java/com/google/android/material/theme/res/values/themes_base.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,12 @@
271271
<item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.Material3.LargeComponent</item>
272272

273273
<!-- Motion attributes.-->
274+
<item name="motionSpringFastSpacial">@style/Motion.Material3.Spring.Standard.Fast.Spacial</item>
275+
<item name="motionSpringFastEffects">@style/Motion.Material3.Spring.Standard.Fast.Effects</item>
276+
<item name="motionSpringDefaultSpacial">@style/Motion.Material3.Spring.Standard.Default.Spacial</item>
277+
<item name="motionSpringDefaultEffects">@style/Motion.Material3.Spring.Standard.Default.Effects</item>
278+
<item name="motionSpringSlowSpacial">@style/Motion.Material3.Spring.Standard.Slow.Spacial</item>
279+
<item name="motionSpringSlowEffects">@style/Motion.Material3.Spring.Standard.Slow.Effects</item>
274280
<item name="motionEasingStandardInterpolator">@interpolator/m3_sys_motion_easing_standard</item>
275281
<item name="motionEasingStandardDecelerateInterpolator">@interpolator/m3_sys_motion_easing_standard_decelerate</item>
276282
<item name="motionEasingStandardAccelerateInterpolator">@interpolator/m3_sys_motion_easing_standard_accelerate</item>
@@ -561,6 +567,12 @@
561567
<item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.Material3.LargeComponent</item>
562568

563569
<!-- Motion attributes. -->
570+
<item name="motionSpringFastSpacial">@style/Motion.Material3.Spring.Standard.Fast.Spacial</item>
571+
<item name="motionSpringFastEffects">@style/Motion.Material3.Spring.Standard.Fast.Effects</item>
572+
<item name="motionSpringDefaultSpacial">@style/Motion.Material3.Spring.Standard.Default.Spacial</item>
573+
<item name="motionSpringDefaultEffects">@style/Motion.Material3.Spring.Standard.Default.Effects</item>
574+
<item name="motionSpringSlowSpacial">@style/Motion.Material3.Spring.Standard.Slow.Spacial</item>
575+
<item name="motionSpringSlowEffects">@style/Motion.Material3.Spring.Standard.Slow.Effects</item>
564576
<item name="motionEasingStandardInterpolator">@interpolator/m3_sys_motion_easing_standard</item>
565577
<item name="motionEasingStandardDecelerateInterpolator">@interpolator/m3_sys_motion_easing_standard_decelerate</item>
566578
<item name="motionEasingStandardAccelerateInterpolator">@interpolator/m3_sys_motion_easing_standard_accelerate</item>

lib/javatests/com/google/android/material/motion/MotionUtilsTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
import com.google.android.material.test.R;
2020

2121
import static com.google.common.truth.Truth.assertThat;
22+
import static org.junit.Assert.assertThrows;
2223

2324
import android.animation.TimeInterpolator;
25+
import android.content.Context;
2426
import android.os.Build.VERSION_CODES;
2527
import androidx.appcompat.app.AppCompatActivity;
2628
import android.view.animation.AccelerateInterpolator;
@@ -29,6 +31,8 @@
2931
import android.view.animation.LinearInterpolator;
3032
import android.view.animation.PathInterpolator;
3133
import androidx.annotation.RequiresApi;
34+
import androidx.core.content.res.ResourcesCompat;
35+
import androidx.dynamicanimation.animation.SpringForce;
3236
import androidx.test.core.app.ApplicationProvider;
3337
import org.junit.Rule;
3438
import org.junit.Test;
@@ -51,6 +55,33 @@ public class MotionUtilsTest {
5155

5256
@Rule public final ExpectedException thrown = ExpectedException.none();
5357

58+
@Test
59+
public void testResolvesThemeSpring() {
60+
createActivityAndSetTheme(R.style.Theme_Material3_DayNight);
61+
Context context = activityController.get().getApplicationContext();
62+
float expectedStiffness = ResourcesCompat.getFloat(
63+
context.getResources(), R.dimen.m3_sys_motion_standard_spring_fast_spatial_stiffness);
64+
float expectedDampingRatio = ResourcesCompat.getFloat(
65+
context.getResources(), R.dimen.m3_sys_motion_standard_spring_fast_spatial_damping);
66+
SpringForce spring =
67+
MotionUtils.resolveThemeSpringForce(context, R.attr.motionSpringFastSpacial);
68+
69+
assertThat(spring.getStiffness()).isEqualTo(expectedStiffness);
70+
assertThat(spring.getDampingRatio()).isEqualTo(expectedDampingRatio);
71+
}
72+
73+
@Test
74+
public void testPartialSpring_shouldThrow() {
75+
createActivityAndSetTheme(R.style.Theme_Material3_DayNight_PartialSpring);
76+
Context context = activityController.get().getApplicationContext();
77+
78+
IllegalArgumentException thrown = assertThrows(
79+
IllegalArgumentException.class,
80+
() -> MotionUtils.resolveThemeSpringForce(context, R.attr.motionSpringFastSpacial)
81+
);
82+
assertThat(thrown).hasMessageThat().contains("must have a damping");
83+
}
84+
5485
@Test
5586
public void testResolvesThemeInterpolator() {
5687
assertThemeInterpolatorIsInstanceOf(

lib/javatests/com/google/android/material/motion/res/values/themes.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
limitations under the License.
1616
-->
1717
<resources>
18+
<!-- A theme with an incorrectly configured custom spring. -->
19+
<style name="Theme.Material3.DayNight.PartialSpring" parent="Theme.Material3.DayNight">
20+
<item name="motionSpringFastSpacial">@style/Motion.MyApp.Spring.Custom.Fast.Spacial</item>
21+
</style>
22+
<style name="Motion.MyApp.Spring.Custom.Fast.Spacial" parent="">
23+
<item name="stiffness">1400</item>
24+
</style>
1825

1926
<style name="Theme.MaterialComponents.DayNight.IncorrectLegacyEasingAttrType" parent="Theme.MaterialComponents.DayNight">
2027
<item name="motionEasingStandard">@color/color_primary</item>

0 commit comments

Comments
 (0)