[ButtonToggleGroup] Fix broken corner morph animation by skipping redundant updates

This fix resolves a regression where the corner morph animation in MaterialButtonToggleGroup was abrupt or interrupted in recent versions (1.14.0-alpha07+).

The issue was caused by unconditional calls to setShapeAppearanceModel() during layout updates. This triggered a reset in MaterialShapeDrawable (due to stricter checks in recent optimizations), cancelling the ongoing animation.

We added a check to compare the calculated corner sizes with the current button's shape. If they are effectively equal, the update is skipped to preserve the ongoing animation.

Closes #4990
This commit is contained in:
jayyaj12 2026-01-14 15:30:26 +09:00
parent 5cb1500477
commit 720ffd6aa1

View File

@ -693,6 +693,21 @@ public class MaterialButtonGroup extends LinearLayout {
originalStateListShapeBuilder
.setCornerSizeOverride(innerCornerSize, cornerPositionBitsToOverride)
.build();
// Pre-calculate the default shape to compare with the current one.
ShapeAppearanceModel newModel = newStateListShape.getDefaultShape(/* withCornerSizeOverrides= */ true);
// Get the current shape of the button.
ShapeAppearanceModel currentModel = button.getShapeAppearanceModel();
// If the corner sizes are effectively the same, skip the update.
// This prevents the MaterialShapeDrawable from resetting its state,
// which would otherwise interrupt the corner morph animation.
if (areCornerSizesEqual(currentModel, newModel)) {
continue;
}
// Apply the new shape if it has changed.
button.setShapeAppearance(
newStateListShape.isStateful()
? newStateListShape
@ -700,6 +715,20 @@ public class MaterialButtonGroup extends LinearLayout {
}
}
/**
* Returns true if the corner sizes of the two {@link ShapeAppearanceModel}s are equal.
*
* <p>This is used to determine if a shape update is necessary, avoiding redundant updates that
* could interrupt ongoing animations.
*/
private static boolean areCornerSizesEqual(
@NonNull ShapeAppearanceModel s1, @NonNull ShapeAppearanceModel s2) {
return s1.getTopLeftCornerSize().equals(s2.getTopLeftCornerSize())
&& s1.getTopRightCornerSize().equals(s2.getTopRightCornerSize())
&& s1.getBottomLeftCornerSize().equals(s2.getBottomLeftCornerSize())
&& s1.getBottomRightCornerSize().equals(s2.getBottomRightCornerSize());
}
/**
* Returns a {@link StateListShapeAppearanceModel.Builder} as the original shape of a child
* button.