Skip to content

Commit

Permalink
[TextField] Fix the issue that menu items gets filtered after recreation
Browse files Browse the repository at this point in the history
It's actually a bug of framework's AutoCompleteTextView. When restoring the
state, it always filters the item list regardless the previously input item
is from menu selection or manual input.

This change fixes the issue by implementing the custom logic of input text
saving/restoring, and properly remember if the last input text filters the
item list or not.

Resolves #1464

PiperOrigin-RevId: 716328564
  • Loading branch information
drchen authored and leticiarossi committed Jan 16, 2025
1 parent 54778a9 commit 690bbfc
Showing 1 changed file with 109 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,14 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.appcompat.widget.AppCompatAutoCompleteTextView;
import androidx.appcompat.widget.ListPopupWindow;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.MeasureSpec;
Expand Down Expand Up @@ -83,6 +88,8 @@ public class MaterialAutoCompleteTextView extends AppCompatAutoCompleteTextView
private int simpleItemSelectedColor;
@Nullable private ColorStateList simpleItemSelectedRippleColor;

@Nullable private CharSequence selectedItem;

public MaterialAutoCompleteTextView(@NonNull Context context) {
this(context, null);
}
Expand Down Expand Up @@ -181,6 +188,23 @@ public void onItemClick(AdapterView<?> parent, View selectedView, int position,
}

attributes.recycle();

// TODO: Remove this workaround once the framework bug (b/202873898) is fixed.
addTextChangedListener(
new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}

@Override
public void afterTextChanged(Editable s) {
if (!TextUtils.equals(selectedItem, s)) {
selectedItem = null;
}
}
});
}

@Override
Expand Down Expand Up @@ -437,6 +461,55 @@ public CharSequence getHint() {
return super.getHint();
}

@Override
public boolean getFreezesText() {
// Always return false to handle the input text restoration by ourselves. This is required
// to avoid the auto-completion from being updated when the view is recreated.
return false;
}

@Override
protected void replaceText(CharSequence text) {
selectedItem = text;
super.replaceText(text);
}

@Override
public void setText(CharSequence text, boolean filter) {
if (!filter) {
// When filter is false, the text is updated by the selection from the auto-complete list.
selectedItem = text;
}
super.setText(text, filter);
}

@Override
@NonNull
public Parcelable onSaveInstanceState() {
Parcelable parcelable = super.onSaveInstanceState();
if (TextUtils.isEmpty(getText()) || !super.getFreezesText()) {
return parcelable;
}

SavedState savedState = new SavedState(parcelable);
// Remember if the current text is from the auto-complete selection.
savedState.shouldRefreshAutoCompletion = (selectedItem == null);
savedState.inputText = getText();
return savedState;
}

@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}

SavedState savedState = (SavedState) state;
setText(savedState.inputText, savedState.shouldRefreshAutoCompletion);
super.onRestoreInstanceState(savedState.getSuperState());
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Expand Down Expand Up @@ -622,4 +695,40 @@ private boolean hasSelectedRippleColor() {
return simpleItemSelectedRippleColor != null;
}
}

private static final class SavedState extends BaseSavedState {

private boolean shouldRefreshAutoCompletion;
private CharSequence inputText;

public SavedState(Parcelable superState) {
super(superState);
}

public SavedState(Parcel source) {
super(source);
shouldRefreshAutoCompletion = source.readInt() != 0;
inputText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}

@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(shouldRefreshAutoCompletion ? 1 : 0);
TextUtils.writeToParcel(inputText, out, flags);
}

public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel source) {
return new SavedState(source);
}

@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

0 comments on commit 690bbfc

Please sign in to comment.