From 7b2f92443ad847c73294d1f5bf2c6406ce3b8850 Mon Sep 17 00:00:00 2001 From: conradchen Date: Tue, 3 May 2022 14:12:42 -0400 Subject: [PATCH] [CleanUp][TextField] Remove TextInputLayout.getEditText() calls from EndIconDelegates PiperOrigin-RevId: 446240046 --- .../textfield/ClearTextEndIconDelegate.java | 70 +++----- .../DropdownMenuEndIconDelegate.java | 152 +++++++----------- .../PasswordToggleEndIconDelegate.java | 43 +++-- 3 files changed, 102 insertions(+), 163 deletions(-) diff --git a/lib/java/com/google/android/material/textfield/ClearTextEndIconDelegate.java b/lib/java/com/google/android/material/textfield/ClearTextEndIconDelegate.java index 675c6bd6cb4..0194d95a309 100644 --- a/lib/java/com/google/android/material/textfield/ClearTextEndIconDelegate.java +++ b/lib/java/com/google/android/material/textfield/ClearTextEndIconDelegate.java @@ -22,9 +22,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.text.Editable; -import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.widget.EditText; @@ -40,26 +38,24 @@ class ClearTextEndIconDelegate extends EndIconDelegate { private static final int ANIMATION_SCALE_DURATION = 150; private static final float ANIMATION_SCALE_FROM_VALUE = 0.8f; + @Nullable + private EditText editText; - private final OnClickListener onIconClickListener = new OnClickListener() { - @Override - public void onClick(View v) { - Editable text = textInputLayout.getEditText().getText(); - if (text != null) { - text.clear(); - } - - endLayout.refreshEndIconDrawableState(); + private final OnClickListener onIconClickListener = view -> { + if (editText == null) { + return; } - }; - - private final OnFocusChangeListener onFocusChangeListener = new OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - animateIcon(shouldBeVisible()); + Editable text = editText.getText(); + if (text != null) { + text.clear(); } + + endLayout.refreshEndIconDrawableState(); }; + private final OnFocusChangeListener onFocusChangeListener = + (view, hasFocus) -> animateIcon(shouldBeVisible()); + private AnimatorSet iconInAnim; private ValueAnimator iconOutAnim; @@ -79,16 +75,10 @@ void setUp() { @Override void tearDown() { - final EditText editText = textInputLayout.getEditText(); if (editText != null) { - editText.post( - new Runnable() { - @Override - public void run() { - // Make sure icon view is visible. - animateIcon(/* show= */ true); - } - }); + editText.post(() -> + // Make sure icon view is visible. + animateIcon(/* show= */ true)); } } @@ -107,6 +97,7 @@ OnClickListener getOnIconClickListener() { @Override public void onEditTextAttached(@Nullable EditText editText) { + this.editText = editText; textInputLayout.setEndIconVisible(shouldBeVisible()); } @@ -171,14 +162,10 @@ private ValueAnimator getAlphaAnimator(float... values) { ValueAnimator animator = ValueAnimator.ofFloat(values); animator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); animator.setDuration(ANIMATION_FADE_DURATION); - animator.addUpdateListener( - new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(@NonNull ValueAnimator animation) { - float alpha = (float) animation.getAnimatedValue(); - endIconView.setAlpha(alpha); - } - }); + animator.addUpdateListener(animation -> { + float alpha = (float) animation.getAnimatedValue(); + endIconView.setAlpha(alpha); + }); return animator; } @@ -187,20 +174,15 @@ private ValueAnimator getScaleAnimator() { ValueAnimator animator = ValueAnimator.ofFloat(ANIMATION_SCALE_FROM_VALUE, 1); animator.setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR); animator.setDuration(ANIMATION_SCALE_DURATION); - animator.addUpdateListener( - new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(@NonNull ValueAnimator animation) { - float scale = (float) animation.getAnimatedValue(); - endIconView.setScaleX(scale); - endIconView.setScaleY(scale); - } - }); + animator.addUpdateListener(animation -> { + float scale = (float) animation.getAnimatedValue(); + endIconView.setScaleX(scale); + endIconView.setScaleY(scale); + }); return animator; } private boolean shouldBeVisible() { - EditText editText = textInputLayout.getEditText(); return editText != null && (editText.hasFocus() || endIconView.hasFocus()) && editText.getText().length() > 0; diff --git a/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java b/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java index ac1b91a6882..6d2633ed7ba 100644 --- a/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java +++ b/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java @@ -26,7 +26,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.SuppressLint; import android.content.Context; import android.os.Build.VERSION; @@ -36,12 +35,9 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; -import android.view.View.OnTouchListener; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener; import android.widget.AutoCompleteTextView; -import android.widget.AutoCompleteTextView.OnDismissListener; import android.widget.EditText; import android.widget.Spinner; import androidx.annotation.ChecksSdkIntAtLeast; @@ -63,6 +59,9 @@ class DropdownMenuEndIconDelegate extends EndIconDelegate { private static final int ANIMATION_FADE_OUT_DURATION = 50; private static final int ANIMATION_FADE_IN_DURATION = 67; + @Nullable + private AutoCompleteTextView autoCompleteTextView; + private final TextInputLayout.AccessibilityDelegate accessibilityDelegate = new AccessibilityDelegate(textInputLayout) { @Override @@ -70,7 +69,7 @@ public void onInitializeAccessibilityNodeInfo( View host, @NonNull AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(host, info); // The non-editable exposed dropdown menu behaves like a Spinner. - if (!isEditable(textInputLayout.getEditText())) { + if (!isEditable(autoCompleteTextView)) { info.setClassName(Spinner.class.getName()); } if (info.isShowingHintText()) { @@ -83,37 +82,24 @@ public void onInitializeAccessibilityNodeInfo( @Override public void onPopulateAccessibilityEvent(View host, @NonNull AccessibilityEvent event) { super.onPopulateAccessibilityEvent(host, event); - AutoCompleteTextView editText = - castAutoCompleteTextViewOrThrow(textInputLayout.getEditText()); - // If dropdown is non editable, layout click is what triggers showing/hiding the popup // list. Otherwise, arrow icon alone is what triggers it. if (event.getEventType() == TYPE_VIEW_CLICKED && accessibilityManager.isEnabled() - && !isEditable(textInputLayout.getEditText())) { - showHideDropdown(editText); + && !isEditable(autoCompleteTextView)) { + showHideDropdown(); updateDropdownPopupDirty(); } } }; + private final OnClickListener onIconClickListener = view -> showHideDropdown(); - private final OnClickListener onIconClickListener = new OnClickListener() { - @Override - public void onClick(View v) { - AutoCompleteTextView editText = (AutoCompleteTextView) textInputLayout.getEditText(); - showHideDropdown(editText); - } - }; - - private final OnFocusChangeListener onEditTextFocusChangeListener = new OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - endLayout.setEndIconActivated(hasFocus); - if (!hasFocus) { - setEndIconChecked(false); - dropdownPopupDirty = false; - } + private final OnFocusChangeListener onEditTextFocusChangeListener = (view, hasFocus) -> { + endLayout.setEndIconActivated(hasFocus); + if (!hasFocus) { + setEndIconChecked(false); + dropdownPopupDirty = false; } }; @@ -144,18 +130,13 @@ void setUp() { accessibilityManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { - accessibilityManager.addTouchExplorationStateChangeListener( - new TouchExplorationStateChangeListener() { - @Override - public void onTouchExplorationStateChanged(boolean enabled) { - if (textInputLayout.getEditText() != null - && !isEditable(textInputLayout.getEditText())) { - ViewCompat.setImportantForAccessibility( - endIconView, - enabled ? IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_YES); - } - } - }); + accessibilityManager.addTouchExplorationStateChangeListener(enabled -> { + if (autoCompleteTextView != null && !isEditable(autoCompleteTextView)) { + ViewCompat.setImportantForAccessibility( + endIconView, + enabled ? IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_YES); + } + }); } } @@ -163,12 +144,11 @@ public void onTouchExplorationStateChanged(boolean enabled) { // interactions with the dropdown menu. @Override void tearDown() { - final AutoCompleteTextView editText = (AutoCompleteTextView) textInputLayout.getEditText(); - if (editText != null) { + if (autoCompleteTextView != null) { // Remove any listeners set on the edit text. - editText.setOnTouchListener(null); + autoCompleteTextView.setOnTouchListener(null); if (IS_LOLLIPOP) { - editText.setOnDismissListener(null); + autoCompleteTextView.setOnDismissListener(null); } } } @@ -190,12 +170,11 @@ OnClickListener getOnIconClickListener() { @Override public void onEditTextAttached(@Nullable EditText editText) { - AutoCompleteTextView autoCompleteTextView = castAutoCompleteTextViewOrThrow(editText); - setUpDropdownShowHideBehavior(autoCompleteTextView); - autoCompleteTextView.setThreshold(0); + this.autoCompleteTextView = castAutoCompleteTextViewOrThrow(editText); + setUpDropdownShowHideBehavior(); textInputLayout.setEndIconCheckable(true); textInputLayout.setErrorIconDrawable(null); - if (!isEditable(autoCompleteTextView) && accessibilityManager.isTouchExplorationEnabled()) { + if (!isEditable(editText) && accessibilityManager.isTouchExplorationEnabled()) { ViewCompat.setImportantForAccessibility(endIconView, IMPORTANT_FOR_ACCESSIBILITY_NO); } textInputLayout.setTextInputAccessibilityDelegate(accessibilityDelegate); @@ -205,23 +184,17 @@ public void onEditTextAttached(@Nullable EditText editText) { @Override public void afterEditTextChanged(Editable s) { - final AutoCompleteTextView editText = - castAutoCompleteTextViewOrThrow(textInputLayout.getEditText()); // Don't show dropdown list if we're in a11y mode and the menu is editable. if (accessibilityManager.isTouchExplorationEnabled() - && isEditable(editText) + && isEditable(autoCompleteTextView) && !endIconView.hasFocus()) { - editText.dismissDropDown(); + autoCompleteTextView.dismissDropDown(); } - editText.post( - new Runnable() { - @Override - public void run() { - boolean isPopupShowing = editText.isPopupShowing(); - setEndIconChecked(isPopupShowing); - dropdownPopupDirty = isPopupShowing; - } - }); + autoCompleteTextView.post(() -> { + boolean isPopupShowing = autoCompleteTextView.isPopupShowing(); + setEndIconChecked(isPopupShowing); + dropdownPopupDirty = isPopupShowing; + }); } @Override @@ -229,8 +202,8 @@ OnFocusChangeListener getOnEditTextFocusChangeListener() { return onEditTextFocusChangeListener; } - private void showHideDropdown(@Nullable AutoCompleteTextView editText) { - if (editText == null) { + private void showHideDropdown() { + if (autoCompleteTextView == null) { return; } if (isDropdownPopupActive()) { @@ -244,10 +217,10 @@ private void showHideDropdown(@Nullable AutoCompleteTextView editText) { endIconView.toggle(); } if (isEndIconChecked) { - editText.requestFocus(); - editText.showDropDown(); + autoCompleteTextView.requestFocus(); + autoCompleteTextView.showDropDown(); } else { - editText.dismissDropDown(); + autoCompleteTextView.dismissDropDown(); } } else { dropdownPopupDirty = false; @@ -256,32 +229,25 @@ private void showHideDropdown(@Nullable AutoCompleteTextView editText) { @SuppressLint("ClickableViewAccessibility") // There's an accessibility delegate that handles // interactions with the dropdown menu. - private void setUpDropdownShowHideBehavior(@NonNull final AutoCompleteTextView editText) { + private void setUpDropdownShowHideBehavior() { // Set whole layout clickable. - editText.setOnTouchListener( - new OnTouchListener() { - @Override - public boolean onTouch(@NonNull View v, @NonNull MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_UP) { - if (isDropdownPopupActive()) { - dropdownPopupDirty = false; - } - showHideDropdown(editText); - updateDropdownPopupDirty(); - } - return false; - } - }); + autoCompleteTextView.setOnTouchListener((view, event) -> { + if (event.getAction() == MotionEvent.ACTION_UP) { + if (isDropdownPopupActive()) { + dropdownPopupDirty = false; + } + showHideDropdown(); + updateDropdownPopupDirty(); + } + return false; + }); if (IS_LOLLIPOP) { - editText.setOnDismissListener( - new OnDismissListener() { - @Override - public void onDismiss() { - updateDropdownPopupDirty(); - setEndIconChecked(false); - } - }); + autoCompleteTextView.setOnDismissListener(() -> { + updateDropdownPopupDirty(); + setEndIconChecked(false); + }); } + autoCompleteTextView.setThreshold(0); } private boolean isDropdownPopupActive() { @@ -330,14 +296,10 @@ private ValueAnimator getAlphaAnimator(int duration, float... values) { ValueAnimator animator = ValueAnimator.ofFloat(values); animator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); animator.setDuration(duration); - animator.addUpdateListener( - new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(@NonNull ValueAnimator animation) { - float alpha = (float) animation.getAnimatedValue(); - endIconView.setAlpha(alpha); - } - }); + animator.addUpdateListener(animation -> { + float alpha = (float) animation.getAnimatedValue(); + endIconView.setAlpha(alpha); + }); return animator; } diff --git a/lib/java/com/google/android/material/textfield/PasswordToggleEndIconDelegate.java b/lib/java/com/google/android/material/textfield/PasswordToggleEndIconDelegate.java index ea93fc1639d..0020ddf4be9 100644 --- a/lib/java/com/google/android/material/textfield/PasswordToggleEndIconDelegate.java +++ b/lib/java/com/google/android/material/textfield/PasswordToggleEndIconDelegate.java @@ -20,7 +20,6 @@ import android.text.InputType; import android.text.method.PasswordTransformationMethod; -import android.view.View; import android.view.View.OnClickListener; import android.widget.EditText; import androidx.annotation.DrawableRes; @@ -30,28 +29,26 @@ /** Default initialization of the password toggle end icon. */ class PasswordToggleEndIconDelegate extends EndIconDelegate { + @Nullable + private EditText editText; - private final OnClickListener onIconClickListener = new OnClickListener() { - @Override - public void onClick(View v) { - EditText editText = textInputLayout.getEditText(); - if (editText == null) { - return; - } - // Store the current cursor position - final int selection = editText.getSelectionEnd(); - if (hasPasswordTransformation()) { - editText.setTransformationMethod(null); - } else { - editText.setTransformationMethod(PasswordTransformationMethod.getInstance()); - } - // And restore the cursor position - if (selection >= 0) { - editText.setSelection(selection); - } - - endLayout.refreshEndIconDrawableState(); + private final OnClickListener onIconClickListener = view -> { + if (editText == null) { + return; + } + // Store the current cursor position + final int selection = editText.getSelectionEnd(); + if (hasPasswordTransformation()) { + editText.setTransformationMethod(null); + } else { + editText.setTransformationMethod(PasswordTransformationMethod.getInstance()); } + // And restore the cursor position + if (selection >= 0) { + editText.setSelection(selection); + } + + endLayout.refreshEndIconDrawableState(); }; PasswordToggleEndIconDelegate( @@ -67,7 +64,6 @@ void setUp() { endLayout.getResources().getText(R.string.password_toggle_content_description)); endLayout.setEndIconVisible(true); endLayout.setEndIconCheckable(true); - EditText editText = textInputLayout.getEditText(); if (isInputTypePassword(editText)) { // By default set the input to be disguised. editText.setTransformationMethod(PasswordTransformationMethod.getInstance()); @@ -76,7 +72,6 @@ void setUp() { @Override void tearDown() { - EditText editText = textInputLayout.getEditText(); if (editText != null) { // Add PasswordTransformation back since it may have been removed to make passwords visible. editText.setTransformationMethod(PasswordTransformationMethod.getInstance()); @@ -90,6 +85,7 @@ OnClickListener getOnIconClickListener() { @Override void onEditTextAttached(@Nullable EditText editText) { + this.editText = editText; endIconView.setChecked(!hasPasswordTransformation()); } @@ -101,7 +97,6 @@ void beforeEditTextChanged(CharSequence s, int start, int count, int after) { } private boolean hasPasswordTransformation() { - EditText editText = textInputLayout.getEditText(); return editText != null && editText.getTransformationMethod() instanceof PasswordTransformationMethod; }