Skip to content


[Carousel] Changed Maskable.add/removeOnMaskChangedListener to Maskab…
Browse files Browse the repository at this point in the history

This change is to prevent the case of items in a RecyclerView.Adapter being recycled and re-bound and having more and more listeners added to a MaskableFrameLayout.

PiperOrigin-RevId: 515048125
  • Loading branch information
hunterstich authored and paulfthomas committed Mar 8, 2023
1 parent c22eb0d commit 359580b
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 22 deletions.
2 changes: 1 addition & 1 deletion docs/components/
Expand Up @@ -88,7 +88,7 @@ The main means of changing the look of carousel is by setting the height of your
If your `RecyclerView`'s item layout contains text or other content that needs to react to changes in the item's mask, you can listen for changes in mask size by setting an `onMaskChangedListener` on your `MaskableFrameLayout` inside your `RecyclerView.ViewHolder`.

(viewHolder.itemView as MaskableFrameLayout).addOnMaskChangedListener(
(viewHolder.itemView as MaskableFrameLayout).setOnMaskChangedListener(
maskRect ->
// Any custom motion to run when mask size changes
Expand Down
17 changes: 9 additions & 8 deletions lib/java/com/google/android/material/carousel/
Expand Up @@ -20,10 +20,9 @@
import android.view.View;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

* Interface for any view that can clip itself and all children to a percentage of its size.
/** Interface for any view that can clip itself and all children to a percentage of its size. */
interface Maskable {

Expand All @@ -45,9 +44,11 @@ interface Maskable {
RectF getMaskRectF();

/** Adds an {@link OnMaskChangedListener}. */
void addOnMaskChangedListener(@NonNull OnMaskChangedListener listener);

/** Removes an {@link OnMaskChangedListener}. */
void removeOnMaskChangedListener(@NonNull OnMaskChangedListener listener);
* Sets an {@link OnMaskChangedListener}.
* @param listener a listener to receive callbacks for changes in the mask or null to clear the
* listener.
void setOnMaskChangedListener(@Nullable OnMaskChangedListener listener);
Expand Up @@ -36,8 +36,6 @@
import androidx.core.math.MathUtils;
import java.util.ArrayList;
import java.util.List;

/** A {@link FrameLayout} than is able to mask itself and all children. */
public class MaskableFrameLayout extends FrameLayout implements Maskable {
Expand All @@ -46,7 +44,7 @@ public class MaskableFrameLayout extends FrameLayout implements Maskable {
private final RectF maskRect = new RectF();
private final Path maskPath = new Path();

private final List<OnMaskChangedListener> onMaskChangedListeners = new ArrayList<>();
@Nullable private OnMaskChangedListener onMaskChangedListener;

private final ShapeAppearanceModel shapeAppearanceModel;

Expand Down Expand Up @@ -105,13 +103,8 @@ public RectF getMaskRectF() {

public void addOnMaskChangedListener(@NonNull OnMaskChangedListener listener) {

public void removeOnMaskChangedListener(@NonNull OnMaskChangedListener listener) {
public void setOnMaskChangedListener(@Nullable OnMaskChangedListener onMaskChangedListener) {
this.onMaskChangedListener = onMaskChangedListener;

private void onMaskChanged() {
Expand All @@ -122,8 +115,8 @@ private void onMaskChanged() {
// masked away.
float maskWidth = AnimationUtils.lerp(0f, getWidth() / 2F, 0f, 1f, maskXPercentage);
maskRect.set(maskWidth, 0F, (getWidth() - maskWidth), getHeight());
for (OnMaskChangedListener listener : onMaskChangedListeners) {
if (onMaskChangedListener != null) {
Expand Down
Expand Up @@ -43,7 +43,7 @@ public class MaskableFrameLayoutTest {
public void testSetMaskXPercentage_shouldTriggerMaskChangedListeners() {
MaskableFrameLayout maskableFrameLayout = createMaskableFrameLayoutWithSize(100, 100);


Expand Down

0 comments on commit 359580b

Please sign in to comment.