33

I have provided an AutoCompleteTextView to show suggestions to the user. Based on the item selected by the user, I am getting the ID of the item and using it in data base side. Now my problem is to force the user to make selection only from AutoCompleteTextView (i.e. user should not enter his own text). It is a client requirement. How to do this?

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Prabhu M
  • 3,534
  • 8
  • 48
  • 87

9 Answers9

34

Here's a pretty straightforward solution:

You can create a variable to store the selected value by setting setOnItemClickListener in your AutoCompleteTextView. Then you can null that value whenever a user types in the field by adding a TextWatcher to it. Finally, you can validate your variable is not null before continuing.

String my_var; //keep track!
AutoCompleteTextView tv = (AutoCompleteTextView) layout.findViewById(R.id.tv);
tv.setAdapter(my_adapter);  
tv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        my_var = my_adapter.getItem(position).toString();
    }
});
/**
 * Unset the var whenever the user types. Validation will
 * then fail. This is how we enforce selecting from the list.
 */
tv.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) {
        my_var = null;
    }
    @Override
    public void afterTextChanged(Editable s) {}
});
rmirabelle
  • 6,268
  • 7
  • 45
  • 42
  • 2
    but if the user typed value that appears in the list the `my_var` shouldn't be null! – dvrm Dec 21 '14 at 09:04
  • 1
    @dvrm then you need to search the adapter for the text the user entered, and select the value from the adapter on each onTextChanged. So, say the user is typing 'name' in the actv, you will then search for 'n', 'na', 'nam', 'name' and when you get a match, choose that value. this way the item in the list will be chosen whether they click it or not, as long as the text matches – regretoverflow Dec 23 '14 at 19:22
  • 1
    great work! now I can keep track whether the user is entering something which is in list or not easily! – Farhan Saikh Sep 12 '19 at 08:15
  • just to add, using `my_var = parent.getItemAtPosition(position).toString();` instead of `my_var = my_adapter.getItem(position).toString();` in the `onItemClick()` also works now. – ganjaam Mar 03 '22 at 10:06
21

I happened to need such a requirement for a project I am working on, and I though I'll share you guys the way I implemented the required.

I added a on focus change listener for the auto-complete text view and checked when the user has focus changed focus from the auto-complete, and handled the situation straight forward.

autoTextViewCountry.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View view, boolean b) {
            if(!b) {
                // on focus off
                String str = autoTextViewCountry.getText().toString();

                ListAdapter listAdapter = autoTextViewCountry.getAdapter();
                for(int i = 0; i < listAdapter.getCount(); i++) {
                    String temp = listAdapter.getItem(i).toString();
                    if(str.compareTo(temp) == 0) {
                        return;
                    }
                }

                autoTextViewCountry.setText("");

            }
        }
    });

So my implementation is: if the typed text doesn't exist in the array adapter then on focus changed empty the text view, and later on when continuing to next stage of say registration, check if this text view is empty or not.

Hope this approach helps somebody.

Happy coding.

NaseemH
  • 408
  • 4
  • 8
  • Looks very neat. If `addTextChangedListener` can be used to fetch list of string then this approach is helpful – AkshayT Apr 04 '18 at 13:52
4

NiceAutoCompleteTextView will give you the ability to check whether a selection was made from the drop-down popup, by invoking isSelectionFromPopup()

Rany Albeg Wein
  • 3,304
  • 3
  • 16
  • 26
2

Just add this property to your AutoCompleteTextView.

android:focusable="false"

My Code looks like:

        <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/menu"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Mode">

        <AutoCompleteTextView
            android:id="@+id/mode"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:focusable="false"
            />

    </com.google.android.material.textfield.TextInputLayout>

On Java Side:

AutoCompleteTextView mode = findViewById(R.id.mode);
   final List<String> modeList = new ArrayList();
    modeList.add("YEARLY");
    modeList.add("HALF-YEARLY");
    modeList.add("QUARTER-YEARLY");
    modeList.add("MONTHLY");
    mode.setAdapter(new ArrayAdapter(getApplicationContext(),R.layout.list_item,modeList));

To get the Text of AutoCompleteTextView:

mode.getText().toString()
MEET DURO
  • 31
  • 3
1

Ok I assume you would like to limit the input of the user to the texts contained in the list of items listed in the suggest box.

For instance if you have:

  • One
  • Two
  • Three

then the user could only type for the first character "O" and "T". And so on according to the text entered before.

To achieve this you can utilize the setFilters method of TextView:

editBox = (TextView) findViewById(R.id.editBox);
editBox.setFilters(getFilters());
editBox.addTextChangedListener(this);
editBox.setOnFocusChangeListener(this);

Additionally you would probably need the text change listener and focus listener to react and update the filtered list when a new character is entered ... plus to update the filter.

Here is an example of decimal number filter I have used on a project:

protected InputFilter[] getFilters()
    {
        InputFilter[] filters = new InputFilter[1];

        filters[0] = new InputFilter()
        {
            public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)
            {
                // limit input to digits and decimal / thousand separator only
                // in case thousand separator is pressed, change it to decimal
                // separator instead
                String output = "";

                if (number.isFocused())
                {
                    for (int i = start; i < end; i++)
                    {
                        char c = source.charAt(i);

                        if (isDecimalOrThousandSeparator(c))
                        {
                            output = output + Character.toString(decimalSeparator);
                        }
                        else if (Character.isDigit(c))
                        {
                            output = output + Character.toString(c);
                        }
                    }

                    return output == "" ? null : output;
                }

                return null;
            }
        };

        return filters;
    }
Drejc
  • 14,196
  • 16
  • 71
  • 106
1

A simple solution would be to just check if the current input is one of the items in the adapter. You can do it like this:

val AutoCompleteTextView.isValid: Boolean
    get() {
        for (i in 0 until adapter.count) {
            if (adapter.getItem(i) == text.toString()) {
                return true
            }
        }

        return false
    }
Max
  • 739
  • 2
  • 8
  • 23
1

here is the another solution for this proAutoCompleteTextView.Validator to ensure the valid values. Validator is invoked when edit text looses focus.

autoCompleteTextView.validator = object : AutoCompleteTextView.Validator {
        override fun isValid(text: CharSequence?): Boolean {
            return optionsList.contains(text.toString())
        }

        override fun fixText(invalidText: CharSequence?): CharSequence {
            return ""
        }
    }

Where optionsList is list of valid values.

Muzammil Husnain
  • 1,218
  • 1
  • 10
  • 24
1

I was having the same requirement, so here is my implementation:

autoCompleteTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                autoCompleteTextView.showDropDown();
            }
        });

In the xml set focusable=false and focusableInTouchMode=false.

Happy coding

whoami - fakeFaceTrueSoul
  • 17,086
  • 6
  • 32
  • 46
Tushar Manohar
  • 153
  • 1
  • 5
0

In order to have a non editable variation of the AutoCompleteTextView, you should disable user input in the AutoCompleteTextView. That can be achieved by setting android:inputType="none" on the AutoCompleteTextView.