4040/** A {@link FrameLayout} than is able to mask itself and all children. */
4141public class MaskableFrameLayout extends FrameLayout implements Maskable , Shapeable {
4242
43- private float maskXPercentage = 0F ;
44- private final RectF maskRect = new RectF ();
43+ private static final float MASK_X_PERCENTAGE_UNSET = -1F ;
44+
45+ private float maskXPercentage = MASK_X_PERCENTAGE_UNSET ;
46+ @ Nullable private RectF maskRect = null ;
4547 @ Nullable private OnMaskChangedListener onMaskChangedListener ;
4648 @ NonNull private ShapeAppearanceModel shapeAppearanceModel ;
4749 private final ShapeableDelegate shapeableDelegate = ShapeableDelegate .create (this );
@@ -65,7 +67,13 @@ public MaskableFrameLayout(
6567 @ Override
6668 protected void onSizeChanged (int w , int h , int oldw , int oldh ) {
6769 super .onSizeChanged (w , h , oldw , oldh );
68- onMaskChanged ();
70+ if (maskXPercentage != MASK_X_PERCENTAGE_UNSET ) {
71+ // If the mask x percentage has been set, the mask rect needs to be recalculated by calling
72+ // setMaskXPercentage which will then handle calling onMaskChanged
73+ setMaskXPercentage (maskXPercentage );
74+ } else {
75+ onMaskChanged ();
76+ }
6977 }
7078
7179 @ Override
@@ -120,23 +128,28 @@ public ShapeAppearanceModel getShapeAppearanceModel() {
120128 @ Override
121129 @ Deprecated
122130 public void setMaskXPercentage (float percentage ) {
123- percentage = MathUtils .clamp (percentage , 0F , 1F );
124- if (maskXPercentage != percentage ) {
125- this .maskXPercentage = percentage ;
126- // Translate the percentage into an actual pixel value of how much of this view should be
127- // masked away.
128- float maskWidth = AnimationUtils .lerp (0f , getWidth () / 2F , 0f , 1f , maskXPercentage );
129- setMaskRectF (new RectF (maskWidth , 0F , (getWidth () - maskWidth ), getHeight ()));
130- }
131+ this .maskXPercentage = MathUtils .clamp (percentage , 0F , 1F );
132+ // Translate the percentage into an actual pixel value of how much of this view should be
133+ // masked away.
134+ float maskWidth = AnimationUtils .lerp (0f , getWidth () / 2F , 0f , 1f , maskXPercentage );
135+ updateMaskRectF (new RectF (maskWidth , 0F , (getWidth () - maskWidth ), getHeight ()));
131136 }
132137
133138 /**
134139 * Sets the {@link RectF} that this {@link View} will be masked by.
135140 *
141+ * <p>Calling this method will overwrite any mask set using {@link #setMaskXPercentage(float)}.
142+ *
136143 * @param maskRect a rect in the view's coordinates to mask by
137144 */
138145 @ Override
139146 public void setMaskRectF (@ NonNull RectF maskRect ) {
147+ this .maskXPercentage = MASK_X_PERCENTAGE_UNSET ;
148+ updateMaskRectF (maskRect );
149+ }
150+
151+ private void updateMaskRectF (@ NonNull RectF maskRect ) {
152+ ensureMaskRectF ();
140153 this .maskRect .set (maskRect );
141154 onMaskChanged ();
142155 }
@@ -158,21 +171,28 @@ public float getMaskXPercentage() {
158171 @ NonNull
159172 @ Override
160173 public RectF getMaskRectF () {
174+ ensureMaskRectF ();
161175 return maskRect ;
162176 }
163177
178+ private void ensureMaskRectF () {
179+ if (maskRect == null ) {
180+ maskRect = new RectF (0F , 0F , getWidth (), getHeight ());
181+ }
182+ }
183+
164184 @ Override
165185 public void setOnMaskChangedListener (@ Nullable OnMaskChangedListener onMaskChangedListener ) {
166186 this .onMaskChangedListener = onMaskChangedListener ;
167187 }
168188
169189 private void onMaskChanged () {
170- if (getWidth () == 0 ) {
190+ if (getWidth () == 0 || getHeight () == 0 ) {
171191 return ;
172192 }
173- shapeableDelegate .onMaskChanged (this , maskRect );
193+ shapeableDelegate .onMaskChanged (this , getMaskRectF () );
174194 if (onMaskChangedListener != null ) {
175- onMaskChangedListener .onMaskChanged (maskRect );
195+ onMaskChangedListener .onMaskChanged (getMaskRectF () );
176196 }
177197 }
178198
@@ -191,10 +211,10 @@ public void setForceCompatClipping(boolean forceCompatClipping) {
191211 @ Override
192212 public boolean onTouchEvent (MotionEvent event ) {
193213 // Only handle touch events that are within the masked bounds of this view.
194- if (!maskRect .isEmpty () && event .getAction () == MotionEvent .ACTION_DOWN ) {
214+ if (!getMaskRectF () .isEmpty () && event .getAction () == MotionEvent .ACTION_DOWN ) {
195215 float x = event .getX ();
196216 float y = event .getY ();
197- if (!maskRect .contains (x , y )) {
217+ if (!getMaskRectF () .contains (x , y )) {
198218 return false ;
199219 }
200220 }
0 commit comments