material-components-android: [Exposed Dropdown Menu] Filtering incorrectly applied after rotation

Description: It seems that in some cases filtering is incorrectly applied to AutocompleteTextView after rotating the device which causes that all options except for the selected one disappear from Dropdown Menu.

Steps to reproduce:

  1. Open Material Catalog app
  2. Go to TextField -> Exposed Dropdown Menu Demo
  3. Tap on 4th TextField from the top and select any value from the dropdown menu.
  4. Rotate the device to landscape and back to portrait
  5. Try to select a value from any of the 4 TextFields

The result is that those Dropdowns are now showing only 1 value instead of all of them.

Expected behavior: All TextField Dropdown Menus should show all values after device rotations

Android API version: Tested on Android 10 and Android 11 Beta

Material Library version: Checked on 1.3.0-alpha01 and 1.2.0-beta01

Device: Google Pixel 3 and Emulator

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 27
  • Comments: 26 (3 by maintainers)

Commits related to this issue

Most upvoted comments

Stumbled upon this issue in our app when fragment view gets recreated from backstack or when a config change happens. We’re using inputType="none" so filtering is not needed. The above adapter filtering solution works for me but i’ve changed the filter slightly to basically do nothing:

class MyAdapter(context: Context, val items: List<Item>)
    : ArrayAdapter<Item>(context, R.layout.layout_item, items) {

    private val noOpFilter = object : Filter() {
        private val noOpResult = FilterResults()
        override fun performFiltering(constraint: CharSequence?) = noOpResult
        override fun publishResults(constraint: CharSequence?, results: FilterResults?) {}
    }

    override fun getFilter() = noOpFilter
}

https://user-images.githubusercontent.com/7274841/192289707-dd0ac1c7-d52a-4b2b-b1e2-8fb7329d7cb8.mp4

To solve the list filtering issue after a configuration change I set

isSaveEnabled = false

when instantiating the drop down menu

full code:

_eqFragmentBinding?.autoCompleteTextView?.run {
    setSimpleItems(mPresetsList.toTypedArray())
    isSaveEnabled = false
    setText(mPresetsList[mSelectedPreset], false)
    setOnItemClickListener { _, _, newPreset, _ ->
        // Respond to item chosen
        mSelectedPreset = newPreset
        mEqualizer?.first?.usePreset(mSelectedPreset.toShort())
        updateBandLevels(isPresetChanged = true)
    }
}

FIX: All the values will be visible after the device rotation and also the selected value will be displayed.

public class TextInputDropDownMenu extends AppCompatAutoCompleteTextView {

    public TextInputDropDownMenu(@NonNull Context context) {
        super(context);
    }

    public TextInputDropDownMenu(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public TextInputDropDownMenu(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    {
        setInputType(InputType.TYPE_NULL);
    }

    @Override
    public boolean getFreezesText() {
        return false;
    }

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

        CustomSavedState customSavedState = new CustomSavedState(parcelable);
        customSavedState.text = getText().toString();
        return customSavedState;
    }

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

        CustomSavedState customSavedState = (CustomSavedState) state;
        setText(customSavedState.text, false);
        super.onRestoreInstanceState(customSavedState.getSuperState());
    }

    private static final class CustomSavedState extends BaseSavedState {

        private String text;

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

        public CustomSavedState(Parcel source) {
            super(source);
            text = source.readString();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeString(text);
        }

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

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

    }

}

Here’s my take on this issue: Instead of creating a custom ArrayAdapter returning a dummy filter, which still launches a background thread to perform the filtering, I create a custom AutoCompleteTextView which always disables filtering when calling setText() if android:inputType="none" (including when restoring view state):

class NonFilterableAutoCompleteTextView @JvmOverloads constructor(context: Context,
                                                                  attributeSet: AttributeSet? = null,
                                                                  defStyleAttr: Int = R.attr.autoCompleteTextViewStyle)
    : MaterialAutoCompleteTextView(context, attributeSet, defStyleAttr) {
    private var isCallingSetText = false

    override fun setText(text: CharSequence?, type: BufferType?) {
        if (isCallingSetText || inputType != EditorInfo.TYPE_NULL) {
            super.setText(text, type)
        } else {
            isCallingSetText = true
            setText(text, false)
            isCallingSetText = false
        }
    }
}

I suggest to include the above code directly in MaterialAutoCompleteTextView to fix the issue, since the class already includes a fix to properly disable editing when android:inputType="none".

Stumbled upon this issue in our app when fragment view gets recreated from backstack or when a config change happens. We’re using inputType="none" so filtering is not needed. The above adapter filtering solution works for me but i’ve changed the filter slightly to basically do nothing:

class MyAdapter(context: Context, val items: List<Item>)
    : ArrayAdapter<Item>(context, R.layout.layout_item, items) {

    private val noOpFilter = object : Filter() {
        private val noOpResult = FilterResults()
        override fun performFiltering(constraint: CharSequence?) = noOpResult
        override fun publishResults(constraint: CharSequence?, results: FilterResults?) {}
    }

    override fun getFilter() = noOpFilter
}

This solution work for me and it’s very easy to implement

I have a same bug when i back via navigation component on my fragment where i allready select some item in dropdown autoCompleteTextView.setFreezesText(false) - This not help me i am avoid bug by next trick: override fun onPause() { super.onPause() etSelectDropdown.setText(“”,false) }

Is there any update on this issue?

@consp1racy it’s reproducible using Material Catalog sample app.

Hello, as of today: 9th Dec 2021, I’m still encountering this issue in AutoCompleteTextView. Has there been any official solution??

Memory Refresh: The issue is that upon screen rotation, autoCompleteTextView drop-down only shows the selected entry instead of the multiple entries.

Any help is highly appreciated.

Can there be some updates for this? Some of the solutions don’t work for me.