From 9789f2e286e5452f9e566a1c0db5b398b56bb4ac Mon Sep 17 00:00:00 2001 From: conradchen Date: Mon, 28 Feb 2022 11:02:11 -0800 Subject: [PATCH] [CleanUp][TextField] Split icon tinting logic to a helper class PiperOrigin-RevId: 431477762 --- .../material/textfield/IconTintHelper.java | 95 +++++++++++++++++++ .../material/textfield/TextInputLayout.java | 87 ++++------------- 2 files changed, 113 insertions(+), 69 deletions(-) create mode 100644 lib/java/com/google/android/material/textfield/IconTintHelper.java diff --git a/lib/java/com/google/android/material/textfield/IconTintHelper.java b/lib/java/com/google/android/material/textfield/IconTintHelper.java new file mode 100644 index 00000000000..4ecd42a82fe --- /dev/null +++ b/lib/java/com/google/android/material/textfield/IconTintHelper.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.material.textfield; + +import android.content.res.ColorStateList; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import androidx.annotation.NonNull; +import androidx.core.graphics.drawable.DrawableCompat; +import com.google.android.material.internal.CheckableImageButton; +import java.util.Arrays; + +class IconTintHelper { + private IconTintHelper() {} + + /** + * Applies the given icon tint according to the merged view state of the host text input layout + * and the icon view. + */ + static void applyIconTint( + @NonNull TextInputLayout textInputLayout, + @NonNull CheckableImageButton iconView, + ColorStateList iconTintList, + PorterDuff.Mode iconTintMode) { + Drawable icon = iconView.getDrawable(); + if (icon != null) { + icon = DrawableCompat.wrap(icon).mutate(); + if (iconTintList != null && iconTintList.isStateful()) { + // Make sure the right color for the current state is applied. + int color = + iconTintList.getColorForState( + mergeIconState(textInputLayout, iconView), iconTintList.getDefaultColor()); + DrawableCompat.setTintList(icon, ColorStateList.valueOf(color)); + } else { + DrawableCompat.setTintList(icon, iconTintList); + } + if (iconTintMode != null) { + DrawableCompat.setTintMode(icon, iconTintMode); + } + } + + if (iconView.getDrawable() != icon) { + iconView.setImageDrawable(icon); + } + } + + /** + * Refresh the icon tint according to the new drawable state. + */ + static void refreshIconDrawableState( + @NonNull TextInputLayout textInputLayout, + @NonNull CheckableImageButton iconView, + ColorStateList colorStateList) { + Drawable icon = iconView.getDrawable(); + if (iconView.getDrawable() == null || colorStateList == null || !colorStateList.isStateful()) { + return; + } + + int color = + colorStateList.getColorForState( + mergeIconState(textInputLayout, iconView), colorStateList.getDefaultColor()); + + icon = DrawableCompat.wrap(icon).mutate(); + DrawableCompat.setTintList(icon, ColorStateList.valueOf(color)); + iconView.setImageDrawable(icon); + } + + private static int[] mergeIconState( + @NonNull TextInputLayout textInputLayout, + @NonNull CheckableImageButton iconView) { + int[] textInputStates = textInputLayout.getDrawableState(); + int[] iconStates = iconView.getDrawableState(); + + int index = textInputStates.length; + int[] states = Arrays.copyOf(textInputStates, textInputStates.length + iconStates.length); + + System.arraycopy(iconStates, 0, states, index, iconStates.length); + + return states; + } +} diff --git a/lib/java/com/google/android/material/textfield/TextInputLayout.java b/lib/java/com/google/android/material/textfield/TextInputLayout.java index 6a5a8cd72e0..ef34f6d57de 100644 --- a/lib/java/com/google/android/material/textfield/TextInputLayout.java +++ b/lib/java/com/google/android/material/textfield/TextInputLayout.java @@ -19,6 +19,8 @@ import com.google.android.material.R; import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; +import static com.google.android.material.textfield.IconTintHelper.applyIconTint; +import static com.google.android.material.textfield.IconTintHelper.refreshIconDrawableState; import static com.google.android.material.textfield.IndicatorViewController.COUNTER_INDEX; import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap; @@ -96,7 +98,6 @@ import com.google.android.material.shape.ShapeAppearanceModel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.LinkedHashSet; /** @@ -2118,7 +2119,7 @@ public void setErrorIconDrawable(@DrawableRes int resId) { public void setErrorIconDrawable(@Nullable Drawable errorIconDrawable) { errorIconView.setImageDrawable(errorIconDrawable); updateErrorIconVisibility(); - applyIconTint(errorIconView, errorIconTintList, errorIconTintMode); + applyIconTint(this, errorIconView, errorIconTintList, errorIconTintMode); } /** @@ -2141,7 +2142,7 @@ public Drawable getErrorIconDrawable() { public void setErrorIconTintList(@Nullable ColorStateList errorIconTintList) { if (this.errorIconTintList != errorIconTintList) { this.errorIconTintList = errorIconTintList; - applyIconTint(errorIconView, this.errorIconTintList, errorIconTintMode); + applyIconTint(this, errorIconView, this.errorIconTintList, errorIconTintMode); } } @@ -2155,7 +2156,7 @@ public void setErrorIconTintList(@Nullable ColorStateList errorIconTintList) { public void setErrorIconTintMode(@Nullable PorterDuff.Mode errorIconTintMode) { if (this.errorIconTintMode != errorIconTintMode) { this.errorIconTintMode = errorIconTintMode; - applyIconTint(errorIconView, errorIconTintList, this.errorIconTintMode); + applyIconTint(this, errorIconView, errorIconTintList, this.errorIconTintMode); } } @@ -3276,7 +3277,7 @@ public void setStartIconDrawable(@DrawableRes int resId) { public void setStartIconDrawable(@Nullable Drawable startIconDrawable) { startIconView.setImageDrawable(startIconDrawable); if (startIconDrawable != null) { - applyIconTint(startIconView, startIconTintList, startIconTintMode); + applyIconTint(this, startIconView, startIconTintList, startIconTintMode); setStartIconVisible(true); refreshStartIconDrawableState(); } else { @@ -3351,7 +3352,7 @@ public boolean isStartIconVisible() { * has a color for a state that depends on a click (such as checked state). */ public void refreshStartIconDrawableState() { - refreshIconDrawableState(startIconView, startIconTintList); + refreshIconDrawableState(this, startIconView, startIconTintList); } /** @@ -3431,7 +3432,7 @@ public CharSequence getStartIconContentDescription() { public void setStartIconTintList(@Nullable ColorStateList startIconTintList) { if (this.startIconTintList != startIconTintList) { this.startIconTintList = startIconTintList; - applyIconTint(startIconView, this.startIconTintList, startIconTintMode); + applyIconTint(this, startIconView, this.startIconTintList, startIconTintMode); } } @@ -3446,7 +3447,7 @@ public void setStartIconTintList(@Nullable ColorStateList startIconTintList) { public void setStartIconTintMode(@Nullable PorterDuff.Mode startIconTintMode) { if (this.startIconTintMode != startIconTintMode) { this.startIconTintMode = startIconTintMode; - applyIconTint(startIconView, startIconTintList, this.startIconTintMode); + applyIconTint(this, startIconView, startIconTintList, this.startIconTintMode); } } @@ -3476,7 +3477,7 @@ public void setEndIconMode(@EndIconMode int endIconMode) { + " is not supported by the end icon mode " + endIconMode); } - applyIconTint(endIconView, endIconTintList, endIconTintMode); + applyIconTint(this, endIconView, endIconTintList, endIconTintMode); } /** @@ -3544,7 +3545,7 @@ public void setErrorIconOnLongClickListener( * has a color for a state that depends on a click (such as checked state). */ public void refreshErrorIconDrawableState() { - refreshIconDrawableState(errorIconView, errorIconTintList); + refreshIconDrawableState(this, errorIconView, errorIconTintList); } /** @@ -3584,7 +3585,7 @@ public void setEndIconActivated(boolean endIconActivated) { * has a color for a state that depends on a click (such as checked state). */ public void refreshEndIconDrawableState() { - refreshIconDrawableState(endIconView, endIconTintList); + refreshIconDrawableState(this, endIconView, endIconTintList); } /** @@ -3637,7 +3638,7 @@ public void setEndIconDrawable(@DrawableRes int resId) { public void setEndIconDrawable(@Nullable Drawable endIconDrawable) { endIconView.setImageDrawable(endIconDrawable); if (endIconDrawable != null) { - applyIconTint(endIconView, endIconTintList, endIconTintMode); + applyIconTint(this, endIconView, endIconTintList, endIconTintMode); refreshEndIconDrawableState(); } } @@ -3707,7 +3708,7 @@ public CharSequence getEndIconContentDescription() { public void setEndIconTintList(@Nullable ColorStateList endIconTintList) { if (this.endIconTintList != endIconTintList) { this.endIconTintList = endIconTintList; - applyIconTint(endIconView, this.endIconTintList, endIconTintMode); + applyIconTint(this, endIconView, this.endIconTintList, endIconTintMode); } } @@ -3722,7 +3723,7 @@ public void setEndIconTintList(@Nullable ColorStateList endIconTintList) { public void setEndIconTintMode(@Nullable PorterDuff.Mode endIconTintMode) { if (this.endIconTintMode != endIconTintMode) { this.endIconTintMode = endIconTintMode; - applyIconTint(endIconView, endIconTintList, this.endIconTintMode); + applyIconTint(this, endIconView, endIconTintList, this.endIconTintMode); } } @@ -3923,7 +3924,7 @@ public void setPasswordVisibilityToggleEnabled(final boolean enabled) { @Deprecated public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintList) { endIconTintList = tintList; - applyIconTint(endIconView, endIconTintList, endIconTintMode); + applyIconTint(this, endIconView, endIconTintList, endIconTintMode); } /** @@ -3938,7 +3939,7 @@ public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintLis @Deprecated public void setPasswordVisibilityToggleTintMode(@Nullable PorterDuff.Mode mode) { endIconTintMode = mode; - applyIconTint(endIconView, endIconTintList, endIconTintMode); + applyIconTint(this, endIconView, endIconTintList, endIconTintMode); } /** @@ -4030,7 +4031,7 @@ private void tintEndIconOnError(boolean tintEndIconOnError) { endIconDrawable, indicatorViewController.getErrorViewCurrentTextColor()); endIconView.setImageDrawable(endIconDrawable); } else { - applyIconTint(endIconView, endIconTintList, endIconTintMode); + applyIconTint(this, endIconView, endIconTintList, endIconTintMode); } } @@ -4138,31 +4139,6 @@ private CheckableImageButton getEndIconToUpdateDummyDrawable() { } } - private void applyIconTint( - @NonNull CheckableImageButton iconView, - ColorStateList iconTintList, - PorterDuff.Mode iconTintMode) { - Drawable icon = iconView.getDrawable(); - if (icon != null) { - icon = DrawableCompat.wrap(icon).mutate(); - if (iconTintList != null && iconTintList.isStateful()) { - // Make sure the right color for the current state is applied. - int color = - iconTintList.getColorForState(mergeIconState(iconView), iconTintList.getDefaultColor()); - DrawableCompat.setTintList(icon, ColorStateList.valueOf(color)); - } else { - DrawableCompat.setTintList(icon, iconTintList); - } - if (iconTintMode != null) { - DrawableCompat.setTintMode(icon, iconTintMode); - } - } - - if (iconView.getDrawable() != icon) { - iconView.setImageDrawable(icon); - } - } - private static void setIconOnClickListener( @NonNull CheckableImageButton iconView, @Nullable OnClickListener onClickListener, @@ -4484,33 +4460,6 @@ private boolean isErrorIconVisible() { return errorIconView.getVisibility() == VISIBLE; } - private void refreshIconDrawableState( - CheckableImageButton iconView, ColorStateList colorStateList) { - Drawable icon = iconView.getDrawable(); - if (iconView.getDrawable() == null || colorStateList == null || !colorStateList.isStateful()) { - return; - } - - int color = - colorStateList.getColorForState(mergeIconState(iconView), colorStateList.getDefaultColor()); - - icon = DrawableCompat.wrap(icon).mutate(); - DrawableCompat.setTintList(icon, ColorStateList.valueOf(color)); - iconView.setImageDrawable(icon); - } - - private int[] mergeIconState(CheckableImageButton iconView) { - int[] textInputStates = this.getDrawableState(); - int[] iconStates = iconView.getDrawableState(); - - int index = textInputStates.length; - int[] states = Arrays.copyOf(textInputStates, textInputStates.length + iconStates.length); - - System.arraycopy(iconStates, 0, states, index, iconStates.length); - - return states; - } - private void expandHint(boolean animate) { if (animator != null && animator.isRunning()) { animator.cancel();