Skip to content

Commit ac74bc3

Browse files
drchendsn5ft
authored andcommitted
[CleanUp][TextField] Centralize icon view state handling logic
Note that there are a slight behavior changes - when a new end icon mode is set, the state will always be reset, if applicable. This should be logically more consistent and correct than the current behavior. PiperOrigin-RevId: 449027939
1 parent 9cd794c commit ac74bc3

File tree

5 files changed

+105
-17
lines changed

5 files changed

+105
-17
lines changed

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ class ClearTextEndIconDelegate extends EndIconDelegate {
4848
if (text != null) {
4949
text.clear();
5050
}
51-
52-
endLayout.refreshEndIconDrawableState();
51+
refreshIconState();
5352
};
5453

5554
private final OnFocusChangeListener onFocusChangeListener =
@@ -64,7 +63,6 @@ class ClearTextEndIconDelegate extends EndIconDelegate {
6463

6564
@Override
6665
void setUp() {
67-
endLayout.setEndIconCheckable(false);
6866
initAnimators();
6967
}
7068

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,18 @@ class DropdownMenuEndIconDelegate extends EndIconDelegate {
6363
private final OnClickListener onIconClickListener = view -> showHideDropdown();
6464

6565
private final OnFocusChangeListener onEditTextFocusChangeListener = (view, hasFocus) -> {
66-
endLayout.setEndIconActivated(hasFocus);
66+
editTextHasFocus = hasFocus;
67+
refreshIconState();
6768
if (!hasFocus) {
6869
setEndIconChecked(false);
6970
dropdownPopupDirty = false;
7071
}
7172
};
7273

73-
private boolean dropdownPopupDirty = false;
74-
private boolean isEndIconChecked = false;
74+
private boolean editTextHasFocus;
75+
76+
private boolean dropdownPopupDirty;
77+
private boolean isEndIconChecked;
7578
private long dropdownPopupActivatedAt = Long.MAX_VALUE;
7679
@Nullable private AccessibilityManager accessibilityManager;
7780
private ValueAnimator fadeOutAnim;
@@ -122,6 +125,26 @@ int getIconContentDescriptionResId() {
122125
return R.string.exposed_dropdown_menu_content_description;
123126
}
124127

128+
@Override
129+
boolean isIconCheckable() {
130+
return true;
131+
}
132+
133+
@Override
134+
boolean isIconChecked() {
135+
return isEndIconChecked;
136+
}
137+
138+
@Override
139+
boolean isIconActivable() {
140+
return true;
141+
}
142+
143+
@Override
144+
boolean isIconActivated() {
145+
return editTextHasFocus;
146+
}
147+
125148
@Override
126149
boolean shouldTintIconOnError() {
127150
return true;
@@ -141,7 +164,6 @@ OnClickListener getOnIconClickListener() {
141164
public void onEditTextAttached(@Nullable EditText editText) {
142165
this.autoCompleteTextView = castAutoCompleteTextViewOrThrow(editText);
143166
setUpDropdownShowHideBehavior();
144-
textInputLayout.setEndIconCheckable(true);
145167
textInputLayout.setErrorIconDrawable(null);
146168
if (!isEditable(editText) && accessibilityManager.isTouchExplorationEnabled()) {
147169
ViewCompat.setImportantForAccessibility(endIconView, IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -207,7 +229,7 @@ private void showHideDropdown() {
207229
setEndIconChecked(!isEndIconChecked);
208230
} else {
209231
isEndIconChecked = !isEndIconChecked;
210-
endIconView.toggle();
232+
refreshIconState();
211233
}
212234
if (isEndIconChecked) {
213235
autoCompleteTextView.requestFocus();
@@ -279,7 +301,7 @@ private void initAnimators() {
279301
new AnimatorListenerAdapter() {
280302
@Override
281303
public void onAnimationEnd(Animator animation) {
282-
endIconView.setChecked(isEndIconChecked);
304+
refreshIconState();
283305
fadeInAnim.start();
284306
}
285307
});

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ void setEndIconMode(@EndIconMode int endIconMode) {
332332
EndIconDelegate delegate = getEndIconDelegate();
333333
setEndIconDrawable(getIconResId(delegate));
334334
setEndIconContentDescription(delegate.getIconContentDescriptionResId());
335+
setEndIconCheckable(delegate.isIconCheckable());
335336
if (delegate.isBoxBackgroundModeSupported(textInputLayout.getBoxBackgroundMode())) {
336337
delegate.setUp();
337338
} else {
@@ -347,6 +348,29 @@ void setEndIconMode(@EndIconMode int endIconMode) {
347348
setOnFocusChangeListenersIfNeeded(delegate);
348349
}
349350
applyIconTint(textInputLayout, endIconView, endIconTintList, endIconTintMode);
351+
refreshIconState(/* force= */ true);
352+
}
353+
354+
void refreshIconState(boolean force) {
355+
boolean stateChanged = false;
356+
EndIconDelegate delegate = getEndIconDelegate();
357+
if (delegate.isIconCheckable()) {
358+
boolean wasChecked = endIconView.isChecked();
359+
if (wasChecked != delegate.isIconChecked()) {
360+
endIconView.setChecked(!wasChecked);
361+
stateChanged = true;
362+
}
363+
}
364+
if (delegate.isIconActivable()) {
365+
boolean wasActivated = endIconView.isActivated();
366+
if (wasActivated != delegate.isIconActivated()) {
367+
setEndIconActivated(!wasActivated);
368+
stateChanged = true;
369+
}
370+
}
371+
if (force || stateChanged) {
372+
refreshEndIconDrawableState();
373+
}
350374
}
351375

352376
private int getIconResId(EndIconDelegate delegate) {

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,40 @@ int getIconContentDescriptionResId() {
7070
return 0;
7171
}
7272

73+
/**
74+
* Returns true if the end icon should be checkable.
75+
*
76+
* @see TextInputLayout#setEndIconCheckable(boolean)
77+
*/
78+
boolean isIconCheckable() {
79+
return false;
80+
}
81+
82+
/**
83+
* Returns true if the end icon should be checked. You will need to override
84+
* {@link #isIconCheckable()} to make this method work.
85+
*/
86+
boolean isIconChecked() {
87+
return false;
88+
}
89+
90+
/**
91+
* Returns true if the end icon should be activable.
92+
*/
93+
boolean isIconActivable() {
94+
return false;
95+
}
96+
97+
/**
98+
* Returns true if the end icon should be activated. You will need to override
99+
* {@link #isIconActivable()} to make this method work.
100+
*
101+
* @see TextInputLayout#setEndIconActivated(boolean)
102+
*/
103+
boolean isIconActivated() {
104+
return false;
105+
}
106+
73107
/**
74108
* Whether the end icon should be tinted with the error color when the {@link TextInputLayout} is
75109
* in error mode.
@@ -147,4 +181,8 @@ void onInitializeAccessibilityNodeInfo(View host, @NonNull AccessibilityNodeInfo
147181
* accessibility event.
148182
*/
149183
void onPopulateAccessibilityEvent(View host, @NonNull AccessibilityEvent event) {}
184+
185+
final void refreshIconState() {
186+
endLayout.refreshIconState(/* force= */ false);
187+
}
150188
}

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ class PasswordToggleEndIconDelegate extends EndIconDelegate {
4949
if (selection >= 0) {
5050
editText.setSelection(selection);
5151
}
52-
53-
endLayout.refreshEndIconDrawableState();
52+
refreshIconState();
5453
};
5554

5655
PasswordToggleEndIconDelegate(
@@ -63,8 +62,6 @@ class PasswordToggleEndIconDelegate extends EndIconDelegate {
6362

6463
@Override
6564
void setUp() {
66-
endLayout.setEndIconVisible(true);
67-
endLayout.setEndIconCheckable(true);
6865
if (isInputTypePassword(editText)) {
6966
// By default set the input to be disguised.
7067
editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
@@ -91,6 +88,17 @@ int getIconContentDescriptionResId() {
9188
return R.string.password_toggle_content_description;
9289
}
9390

91+
@Override
92+
boolean isIconCheckable() {
93+
return true;
94+
}
95+
96+
@Override
97+
boolean isIconChecked() {
98+
// Make sure the password toggle state always matches the EditText's transformation method.
99+
return !hasPasswordTransformation();
100+
}
101+
94102
@Override
95103
OnClickListener getOnIconClickListener() {
96104
return onIconClickListener;
@@ -99,14 +107,12 @@ OnClickListener getOnIconClickListener() {
99107
@Override
100108
void onEditTextAttached(@Nullable EditText editText) {
101109
this.editText = editText;
102-
endIconView.setChecked(!hasPasswordTransformation());
110+
refreshIconState();
103111
}
104112

105113
@Override
106114
void beforeEditTextChanged(CharSequence s, int start, int count, int after) {
107-
// Make sure the password toggle state always matches the EditText's transformation
108-
// method.
109-
endIconView.setChecked(!hasPasswordTransformation());
115+
refreshIconState();
110116
}
111117

112118
private boolean hasPasswordTransformation() {

0 commit comments

Comments
 (0)