Skip to content

Commit 9789f2e

Browse files
drchenhunterstich
authored andcommitted
[CleanUp][TextField] Split icon tinting logic to a helper class
PiperOrigin-RevId: 431477762
1 parent 3b2b56e commit 9789f2e

File tree

2 files changed

+113
-69
lines changed

2 files changed

+113
-69
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (C) 2022 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.android.material.textfield;
18+
19+
import android.content.res.ColorStateList;
20+
import android.graphics.PorterDuff;
21+
import android.graphics.drawable.Drawable;
22+
import androidx.annotation.NonNull;
23+
import androidx.core.graphics.drawable.DrawableCompat;
24+
import com.google.android.material.internal.CheckableImageButton;
25+
import java.util.Arrays;
26+
27+
class IconTintHelper {
28+
private IconTintHelper() {}
29+
30+
/**
31+
* Applies the given icon tint according to the merged view state of the host text input layout
32+
* and the icon view.
33+
*/
34+
static void applyIconTint(
35+
@NonNull TextInputLayout textInputLayout,
36+
@NonNull CheckableImageButton iconView,
37+
ColorStateList iconTintList,
38+
PorterDuff.Mode iconTintMode) {
39+
Drawable icon = iconView.getDrawable();
40+
if (icon != null) {
41+
icon = DrawableCompat.wrap(icon).mutate();
42+
if (iconTintList != null && iconTintList.isStateful()) {
43+
// Make sure the right color for the current state is applied.
44+
int color =
45+
iconTintList.getColorForState(
46+
mergeIconState(textInputLayout, iconView), iconTintList.getDefaultColor());
47+
DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
48+
} else {
49+
DrawableCompat.setTintList(icon, iconTintList);
50+
}
51+
if (iconTintMode != null) {
52+
DrawableCompat.setTintMode(icon, iconTintMode);
53+
}
54+
}
55+
56+
if (iconView.getDrawable() != icon) {
57+
iconView.setImageDrawable(icon);
58+
}
59+
}
60+
61+
/**
62+
* Refresh the icon tint according to the new drawable state.
63+
*/
64+
static void refreshIconDrawableState(
65+
@NonNull TextInputLayout textInputLayout,
66+
@NonNull CheckableImageButton iconView,
67+
ColorStateList colorStateList) {
68+
Drawable icon = iconView.getDrawable();
69+
if (iconView.getDrawable() == null || colorStateList == null || !colorStateList.isStateful()) {
70+
return;
71+
}
72+
73+
int color =
74+
colorStateList.getColorForState(
75+
mergeIconState(textInputLayout, iconView), colorStateList.getDefaultColor());
76+
77+
icon = DrawableCompat.wrap(icon).mutate();
78+
DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
79+
iconView.setImageDrawable(icon);
80+
}
81+
82+
private static int[] mergeIconState(
83+
@NonNull TextInputLayout textInputLayout,
84+
@NonNull CheckableImageButton iconView) {
85+
int[] textInputStates = textInputLayout.getDrawableState();
86+
int[] iconStates = iconView.getDrawableState();
87+
88+
int index = textInputStates.length;
89+
int[] states = Arrays.copyOf(textInputStates, textInputStates.length + iconStates.length);
90+
91+
System.arraycopy(iconStates, 0, states, index, iconStates.length);
92+
93+
return states;
94+
}
95+
}

lib/java/com/google/android/material/textfield/TextInputLayout.java

Lines changed: 18 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.google.android.material.R;
2020

2121
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
22+
import static com.google.android.material.textfield.IconTintHelper.applyIconTint;
23+
import static com.google.android.material.textfield.IconTintHelper.refreshIconDrawableState;
2224
import static com.google.android.material.textfield.IndicatorViewController.COUNTER_INDEX;
2325
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
2426

@@ -96,7 +98,6 @@
9698
import com.google.android.material.shape.ShapeAppearanceModel;
9799
import java.lang.annotation.Retention;
98100
import java.lang.annotation.RetentionPolicy;
99-
import java.util.Arrays;
100101
import java.util.LinkedHashSet;
101102

102103
/**
@@ -2118,7 +2119,7 @@ public void setErrorIconDrawable(@DrawableRes int resId) {
21182119
public void setErrorIconDrawable(@Nullable Drawable errorIconDrawable) {
21192120
errorIconView.setImageDrawable(errorIconDrawable);
21202121
updateErrorIconVisibility();
2121-
applyIconTint(errorIconView, errorIconTintList, errorIconTintMode);
2122+
applyIconTint(this, errorIconView, errorIconTintList, errorIconTintMode);
21222123
}
21232124

21242125
/**
@@ -2141,7 +2142,7 @@ public Drawable getErrorIconDrawable() {
21412142
public void setErrorIconTintList(@Nullable ColorStateList errorIconTintList) {
21422143
if (this.errorIconTintList != errorIconTintList) {
21432144
this.errorIconTintList = errorIconTintList;
2144-
applyIconTint(errorIconView, this.errorIconTintList, errorIconTintMode);
2145+
applyIconTint(this, errorIconView, this.errorIconTintList, errorIconTintMode);
21452146
}
21462147
}
21472148

@@ -2155,7 +2156,7 @@ public void setErrorIconTintList(@Nullable ColorStateList errorIconTintList) {
21552156
public void setErrorIconTintMode(@Nullable PorterDuff.Mode errorIconTintMode) {
21562157
if (this.errorIconTintMode != errorIconTintMode) {
21572158
this.errorIconTintMode = errorIconTintMode;
2158-
applyIconTint(errorIconView, errorIconTintList, this.errorIconTintMode);
2159+
applyIconTint(this, errorIconView, errorIconTintList, this.errorIconTintMode);
21592160
}
21602161
}
21612162

@@ -3276,7 +3277,7 @@ public void setStartIconDrawable(@DrawableRes int resId) {
32763277
public void setStartIconDrawable(@Nullable Drawable startIconDrawable) {
32773278
startIconView.setImageDrawable(startIconDrawable);
32783279
if (startIconDrawable != null) {
3279-
applyIconTint(startIconView, startIconTintList, startIconTintMode);
3280+
applyIconTint(this, startIconView, startIconTintList, startIconTintMode);
32803281
setStartIconVisible(true);
32813282
refreshStartIconDrawableState();
32823283
} else {
@@ -3351,7 +3352,7 @@ public boolean isStartIconVisible() {
33513352
* has a color for a state that depends on a click (such as checked state).
33523353
*/
33533354
public void refreshStartIconDrawableState() {
3354-
refreshIconDrawableState(startIconView, startIconTintList);
3355+
refreshIconDrawableState(this, startIconView, startIconTintList);
33553356
}
33563357

33573358
/**
@@ -3431,7 +3432,7 @@ public CharSequence getStartIconContentDescription() {
34313432
public void setStartIconTintList(@Nullable ColorStateList startIconTintList) {
34323433
if (this.startIconTintList != startIconTintList) {
34333434
this.startIconTintList = startIconTintList;
3434-
applyIconTint(startIconView, this.startIconTintList, startIconTintMode);
3435+
applyIconTint(this, startIconView, this.startIconTintList, startIconTintMode);
34353436
}
34363437
}
34373438

@@ -3446,7 +3447,7 @@ public void setStartIconTintList(@Nullable ColorStateList startIconTintList) {
34463447
public void setStartIconTintMode(@Nullable PorterDuff.Mode startIconTintMode) {
34473448
if (this.startIconTintMode != startIconTintMode) {
34483449
this.startIconTintMode = startIconTintMode;
3449-
applyIconTint(startIconView, startIconTintList, this.startIconTintMode);
3450+
applyIconTint(this, startIconView, startIconTintList, this.startIconTintMode);
34503451
}
34513452
}
34523453

@@ -3476,7 +3477,7 @@ public void setEndIconMode(@EndIconMode int endIconMode) {
34763477
+ " is not supported by the end icon mode "
34773478
+ endIconMode);
34783479
}
3479-
applyIconTint(endIconView, endIconTintList, endIconTintMode);
3480+
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
34803481
}
34813482

34823483
/**
@@ -3544,7 +3545,7 @@ public void setErrorIconOnLongClickListener(
35443545
* has a color for a state that depends on a click (such as checked state).
35453546
*/
35463547
public void refreshErrorIconDrawableState() {
3547-
refreshIconDrawableState(errorIconView, errorIconTintList);
3548+
refreshIconDrawableState(this, errorIconView, errorIconTintList);
35483549
}
35493550

35503551
/**
@@ -3584,7 +3585,7 @@ public void setEndIconActivated(boolean endIconActivated) {
35843585
* has a color for a state that depends on a click (such as checked state).
35853586
*/
35863587
public void refreshEndIconDrawableState() {
3587-
refreshIconDrawableState(endIconView, endIconTintList);
3588+
refreshIconDrawableState(this, endIconView, endIconTintList);
35883589
}
35893590

35903591
/**
@@ -3637,7 +3638,7 @@ public void setEndIconDrawable(@DrawableRes int resId) {
36373638
public void setEndIconDrawable(@Nullable Drawable endIconDrawable) {
36383639
endIconView.setImageDrawable(endIconDrawable);
36393640
if (endIconDrawable != null) {
3640-
applyIconTint(endIconView, endIconTintList, endIconTintMode);
3641+
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
36413642
refreshEndIconDrawableState();
36423643
}
36433644
}
@@ -3707,7 +3708,7 @@ public CharSequence getEndIconContentDescription() {
37073708
public void setEndIconTintList(@Nullable ColorStateList endIconTintList) {
37083709
if (this.endIconTintList != endIconTintList) {
37093710
this.endIconTintList = endIconTintList;
3710-
applyIconTint(endIconView, this.endIconTintList, endIconTintMode);
3711+
applyIconTint(this, endIconView, this.endIconTintList, endIconTintMode);
37113712
}
37123713
}
37133714

@@ -3722,7 +3723,7 @@ public void setEndIconTintList(@Nullable ColorStateList endIconTintList) {
37223723
public void setEndIconTintMode(@Nullable PorterDuff.Mode endIconTintMode) {
37233724
if (this.endIconTintMode != endIconTintMode) {
37243725
this.endIconTintMode = endIconTintMode;
3725-
applyIconTint(endIconView, endIconTintList, this.endIconTintMode);
3726+
applyIconTint(this, endIconView, endIconTintList, this.endIconTintMode);
37263727
}
37273728
}
37283729

@@ -3923,7 +3924,7 @@ public void setPasswordVisibilityToggleEnabled(final boolean enabled) {
39233924
@Deprecated
39243925
public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintList) {
39253926
endIconTintList = tintList;
3926-
applyIconTint(endIconView, endIconTintList, endIconTintMode);
3927+
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
39273928
}
39283929

39293930
/**
@@ -3938,7 +3939,7 @@ public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintLis
39383939
@Deprecated
39393940
public void setPasswordVisibilityToggleTintMode(@Nullable PorterDuff.Mode mode) {
39403941
endIconTintMode = mode;
3941-
applyIconTint(endIconView, endIconTintList, endIconTintMode);
3942+
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
39423943
}
39433944

39443945
/**
@@ -4030,7 +4031,7 @@ private void tintEndIconOnError(boolean tintEndIconOnError) {
40304031
endIconDrawable, indicatorViewController.getErrorViewCurrentTextColor());
40314032
endIconView.setImageDrawable(endIconDrawable);
40324033
} else {
4033-
applyIconTint(endIconView, endIconTintList, endIconTintMode);
4034+
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
40344035
}
40354036
}
40364037

@@ -4138,31 +4139,6 @@ private CheckableImageButton getEndIconToUpdateDummyDrawable() {
41384139
}
41394140
}
41404141

4141-
private void applyIconTint(
4142-
@NonNull CheckableImageButton iconView,
4143-
ColorStateList iconTintList,
4144-
PorterDuff.Mode iconTintMode) {
4145-
Drawable icon = iconView.getDrawable();
4146-
if (icon != null) {
4147-
icon = DrawableCompat.wrap(icon).mutate();
4148-
if (iconTintList != null && iconTintList.isStateful()) {
4149-
// Make sure the right color for the current state is applied.
4150-
int color =
4151-
iconTintList.getColorForState(mergeIconState(iconView), iconTintList.getDefaultColor());
4152-
DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
4153-
} else {
4154-
DrawableCompat.setTintList(icon, iconTintList);
4155-
}
4156-
if (iconTintMode != null) {
4157-
DrawableCompat.setTintMode(icon, iconTintMode);
4158-
}
4159-
}
4160-
4161-
if (iconView.getDrawable() != icon) {
4162-
iconView.setImageDrawable(icon);
4163-
}
4164-
}
4165-
41664142
private static void setIconOnClickListener(
41674143
@NonNull CheckableImageButton iconView,
41684144
@Nullable OnClickListener onClickListener,
@@ -4484,33 +4460,6 @@ private boolean isErrorIconVisible() {
44844460
return errorIconView.getVisibility() == VISIBLE;
44854461
}
44864462

4487-
private void refreshIconDrawableState(
4488-
CheckableImageButton iconView, ColorStateList colorStateList) {
4489-
Drawable icon = iconView.getDrawable();
4490-
if (iconView.getDrawable() == null || colorStateList == null || !colorStateList.isStateful()) {
4491-
return;
4492-
}
4493-
4494-
int color =
4495-
colorStateList.getColorForState(mergeIconState(iconView), colorStateList.getDefaultColor());
4496-
4497-
icon = DrawableCompat.wrap(icon).mutate();
4498-
DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
4499-
iconView.setImageDrawable(icon);
4500-
}
4501-
4502-
private int[] mergeIconState(CheckableImageButton iconView) {
4503-
int[] textInputStates = this.getDrawableState();
4504-
int[] iconStates = iconView.getDrawableState();
4505-
4506-
int index = textInputStates.length;
4507-
int[] states = Arrays.copyOf(textInputStates, textInputStates.length + iconStates.length);
4508-
4509-
System.arraycopy(iconStates, 0, states, index, iconStates.length);
4510-
4511-
return states;
4512-
}
4513-
45144463
private void expandHint(boolean animate) {
45154464
if (animator != null && animator.isRunning()) {
45164465
animator.cancel();

0 commit comments

Comments
 (0)