58

I have an AutoCompleteTextView that is filled with cities from an sqlite database that calls an AsyncTask on item click, recently I added an option to detect my location using the gps, so the problem is I can detect the city (i.e Beirut) and set the text for the AutoCompleteTextView but the thing is that the dropdown filter opens showing Beirut (which is correct) but I still need to click on the list item to invoke the listener, how to do so programmatically

How to:

  • Enter the Activity (DONE)
  • Detect location (DONE)
  • set text of text view (DONE)
  • show textview dropdown list(DONE)
  • choose the item that will be returned, since it will only return one city (NOT DONE)
Hussein Yassine
  • 595
  • 1
  • 4
  • 6

9 Answers9

137

To be clear, Tano's solution is sufficient to answer this question. But, in case others run into the same use case I did, here's some more background that may potentially help you...

I had been running into this issue specifically while trying to make a non-editable Material Exposed Dropdown Menu and set it's initial value programmatically. The documentation to create this type of "dropdown" can be found in the Exposed Dropdown Menus section here, which suggests a mechanism using TextInputLayout and AutocompleteTextView (even if you don't want autocomplete functionality).

Failed Solution 1: At first glance setListSelection() and getListSelection() seemed like they might do the trick. But after many trials, I learned that they may not be sufficient because they only work when the list popup isShowing(). So for example, if you simply want to set the initial selection without having to show the list popup first, this will not work.

Failed Solution 2: Then I tried setText() which showed the proper text in my textbox. Yay! But wait! When I clicked on the text view, only a subset of options in the list popup were shown for some reason. Why was that? The key thing to keep in mind here is that since this is an autocomplete textview, it by default filters out options based off of the text in the textview. This might not be apparent, especially if you're solely using this control for the sake of making a simple non-editable dropdown selector.

Solution: This brings us to our actual solution (suggested by Tano)... setText() with filter as false will turn off the filtering capabilities AND it will not change the contents of your list popup.

autoCompleteTextView.setText(myText, false);
JHowzer
  • 3,684
  • 4
  • 30
  • 36
  • I am also trying to use the autocomplete text view to create a nice dropdown that behaves like a Spinner (minus the annoying listeners from firing but with the nice outlined look the textinputlayout provides). The solutions here work to set the selected item based on text, but I would like all the options available when you touch the autocomplete. Right now I would still have to delete the text to have all of them appear. Any ideas? Or is there a way to make a Spinner look like the outlined textinputlayout? – JPM Jul 26 '19 at 02:02
  • 3
    This is a brilliant answer and well explained. The only problem I have is that the filter option requires an API level of 17 – dave o grady Mar 03 '20 at 00:41
  • @JPM, you can show a drop-down list at any time, see https://stackoverflow.com/a/26036902/2914140. Currently it opens full list (first time) even if some text has entered in. When edit a text, it filters items. Maybe https://stackoverflow.com/a/60791879/2914140 works, didn't check. – CoolMind Jun 02 '20 at 12:39
  • 2
    I spent a couple of hours on the Failed Solutions mentioned here, and now I am this close to a career change after developing Android apps for 8 years. – SafaOrhan Oct 01 '21 at 15:25
75

I was facing a similar problem and this solved my issue. Important is to call setText(<text>, <filter boolean>) in order not to filter with the given text set the second parameter with false. The text will be got from the dropdown adapter.

Solution snippet:

automCompleteTextView.setText(automCompleteTextView.getAdapter().getItem(position).toString(), false);
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Tano
  • 3,620
  • 1
  • 29
  • 31
  • 1
    Thanks! I think, `false` is a default value. So, we can use `autoCompleteTextView.setText(someText);`. – CoolMind Jun 02 '20 at 13:01
21

A solution were you don't need to change your API level.

automCompleteTextView.setAdapter(adapter);        
// set default selection, filtering is active so all items is not visible in drop-down menu
automCompleteTextView.setText(automCompleteTextView.getAdapter().getItem(0).toString());
// change filtering for the adapter so all items can be visible in drop-down menu
adapter.getFilter().filter(null);

one-liner for the same job but requires higher API level

automCompleteTextView.setText(automCompleteTextView.getAdapter().getItem(0).toString(), false);
user1564762
  • 745
  • 2
  • 11
  • 18
7

I figure out after dig into the AutoCompleteTextView code on android source code:

fun AutoCompleteTextView.selectItem(text: String, position: Int = 0) {
  this.setText(text)
  this.showDropDown()
  this.setSelection(position)
  this.listSelection = position
  this.performCompletion()
}
Nilton Vasques
  • 539
  • 5
  • 10
  • 1
    Thanks! I removed `setSelection(position)`, because it threw `java.lang.IndexOutOfBoundsException: setSpan (43 ... 43) ends beyond length 34`. Also `this.` is unneccessary. – CoolMind Jun 02 '20 at 13:06
3
autoComplete.setListSelection(position);
Robson Chico
  • 121
  • 1
  • 3
2

I have used autoCompleteTextView.setText(myText, false); solution as well, however it sometimes failed. I mean it was actively filtering results so, when user clicks there was only 1 item at dropdown.

In addition I also needed this to work on custom objects as well, and this is my my solution:

binding.hourEditText.configureDropDownMenu(viewModel.hours) { it.hourString() }
    .subscribe {
        // Do whatever you need when on click.
    }
    .addTo(disposables)

fun <T> AutoCompleteTextView.configureDropDownMenu(list: List<T>, toString: ((T) -> String)? = null): Observable<T> {
    keyListener = null
    val textItems = toString?.let(list::map) ?: list.map { it.toString() }
    setAdapter(NonFilterArrayAdapter(context!!, android.R.layout.simple_spinner_dropdown_item, textItems))
    return itemClickEvents().map {
        list[it.position]
    }
}

private class NonFilterArrayAdapter<T>(context: Context, @LayoutRes resource: Int, objects: List<T>) : ArrayAdapter<T>(context, resource, objects) {

    override fun getFilter() = NonFilter()

    private class NonFilter : Filter() {
        override fun performFiltering(constraint: CharSequence?) = FilterResults()

        override fun publishResults(constraint: CharSequence?, results: FilterResults?) = Unit
    }
}

Note: This also contains a bit of Rx, but it can be removed easily.

guness
  • 6,336
  • 7
  • 59
  • 88
0

Try with adding below after setText() in AutoCompleteTextview:-

autoCompleteTV.setSelection(position);

Updated:

This will work in Spinner and AutoCompleteTextView which has dropdown feature, but it will not work with EditText.

Here you can check docs for AbsSpinner in this link: https://developer.android.com/reference/android/widget/AbsSpinner.html#setSelection(int)

Nitin Patel
  • 1,605
  • 13
  • 31
  • @HusseinYassine Let me check in my work and will back with solution. – Nitin Patel Jul 07 '17 at 06:35
  • 4
    setSelection is short hand for where to place the cursor not selecting an item in the Adapter. – Chris.Jenkins Aug 18 '19 at 22:15
  • @Chris.Jenkins For your clarification, check this link https://developer.android.com/reference/android/widget/AbsSpinner.html#setSelection(int) and still did not get then after initialization just try with different position. – Nitin Patel Aug 19 '19 at 12:39
  • 1
    @NitinPatel Yes that is for an `AbstractSpinner`, `AutoCompleteTextView` is not that. `setSelection` on an `EditText` sets the cursor position not the selection of item in a spinner. – Chris.Jenkins Aug 20 '19 at 14:01
  • @Chris.Jenkins I agree with you. That is the secret. – Nitin Patel Aug 20 '19 at 14:59
  • 1
    This can lead to a crash: 'java.lang.IndexOutOfBoundsException: setSpan (43 ... 43) ends beyond length 34'. Use `setListSelection` instead. – CoolMind Jun 02 '20 at 13:44
0

The problem is that you are setting a text and the AutoCompleteTextView is only showing words that match with that text. A non elegant way of solving this is to set an high threshold (at least the max length of the names of the cities) to force Android to show you all the values of your list (this threshold is the number of characters that the field must have to search similarities).

David M.R.
  • 15
  • 6
0

Using the Nilton Vasques solution it can be so:

with(autoComplete) {
    setAdapter(this@YourFragment.adapter)
    setText(itemText)
    showDropDown()
    listSelection = if (itemIndex > 0) itemIndex - 1 else 0 // Because AutoCompleteTextView shows the next row.
    performCompletion()
}

Notice, that it will show a drop-down list, otherwise listSelection won't work. If you call dismissDropDown(), the item won't be selected. If you don't want to show the drop-down list, you can use setOnTouchListener to capture opening the list, but it hardly will help (you should resolve a filtering problem).

setOnTouchListener { _, event ->
    if (event.action == MotionEvent.ACTION_DOWN) {
        showDropDown()
        listSelection = if (itemIndex > 0) itemIndex - 1 else 0
        performCompletion()
        requestFocus()
    }
    false
}
CoolMind
  • 26,736
  • 15
  • 188
  • 224