If you ever encounter AutoCompleteTextView in your Android project, you must have had the following questions in your mind.
– How to provide data for AutoCompleteTextView
– How to design row item for AutoCompleteTextView
– How to check which item selected in AutoCompleteTextView
– How to populate AutoCompleteTextView from an array of data class without implementing an adapter
– How to change search(filter) preferences
– etc…
In this tutorial, I will answer all your questions.
Here we use Kotlin language for creating this example.
First of all, we will create the layout for the AutoCompleteTextView.
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context="com.acomputerengineer.AutoCompleteTextViewActivity"> <com.google.android.material.textfield.TextInputLayout android:id="@+id/til" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <AutoCompleteTextView android:id="@+id/actv" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/your_hint_here" android:inputType="text" android:maxLines="1" /> </com.google.android.material.textfield.TextInputLayout> </androidx.constraintlayout.widget.ConstraintLayout>
Now we will see 3 different cases for AutoCompleteTextView.
First Case
Here we use array of strings to populate AutoCompleteTextView and display list.
First, we will create the list of movies as a string array and display it in AutoCompleteTextView using the default ArrayAdapter.
val movies = arrayOf("Avengers: Endgame", "Captain Marvel", "Shazam!", "Spider-Man: Far From Home", "Dark Phoenix", "Hellboy", "Glass", "Reign of the Superman", "Brightburn") val adapter1 = ArrayAdapter(this, android.R.layout.select_dialog_item, movies) //actv is the AutoCompleteTextView from your layout file actv.threshold = 1 //start searching for values after typing first character actv.setAdapter(adapter1)
Here is the result:
Second Case
Here we use an array of a custom object of data class to populate AutoCompleteTextView and display list.
We will create the list of movies as a Movie data class array and display it in AutoCompleteTextView using the default ArrayAdapter. The ArrayAdapter uses “toString()” method of a data class if there is data class array provided.
So first we will create a data class named Movie.
data class Movie( val name: String, val year: Int ) { override fun toString(): String { return name.plus(" - ").plus(year) } }
Now we will create the adapter and set it to the AutoCompleteTextView.
val movieObjects = arrayOf(Movie("Avengers: Endgame", 2019), Movie("Captain Marvel", 2019), Movie("Shazam!", 2019), Movie("Spider-Man: Far From Home", 2019), Movie("Dark Phoenix", 2019), Movie("Hellboy", 2019), Movie("Glass", 2019), Movie("Reign of the Superman", 2019), Movie("Brightburn", 2019)) val adapter2 = ArrayAdapter(this, android.R.layout.select_dialog_item, movieObjects) //actv is the AutoCompleteTextView from your layout file actv.threshold = 1 //start searching for values after typing first character actv.setAdapter(adapter2)
Here is the result:
Now in both above case, you may have noticed that the design for the row is single text line provided in ‘android.R.layout.select_dialog_item’ layout. If we want to create a custom layout, we need to create a custom ArrayAdapter and inflate our layout there.
You may also have noticed that here when I type “ma”, it only displays one result “Captain Marvel” because the default filter uses “startsWith()” function so it only selects the result which has words start with “ma”. So to check in the whole string, we need to create a custom filter in the ArrayAdapter. So let’s see that.
Third Case
Here first we create an adapter that extends ArrayAdapter and in that we will inflate our custom layout and also add logic for our filter method.
class AutoCompleteTextViewAdapter(private val c: Context, @LayoutRes private val layoutResource: Int, private val movies: Array<AutoCompleteTextViewActivity.Movie>) : ArrayAdapter<AutoCompleteTextViewActivity.Movie>(c, layoutResource, movies) { var filteredMovies: List<AutoCompleteTextViewActivity.Movie> = listOf() override fun getCount(): Int = filteredMovies.size override fun getItem(position: Int): AutoCompleteTextViewActivity.Movie = filteredMovies[position] override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { val view = convertView ?: LayoutInflater.from(c).inflate(layoutResource, parent, false) view.tvMovieName.text = filteredMovies[position].name view.tvMovieYear.text = filteredMovies[position].year.toString() return view } override fun getFilter(): Filter { return object : Filter() { override fun publishResults(charSequence: CharSequence?, filterResults: FilterResults) { @Suppress("UNCHECKED_CAST") filteredMovies = filterResults.values as List<AutoCompleteTextViewActivity.Movie> notifyDataSetChanged() } override fun performFiltering(charSequence: CharSequence?): FilterResults { val queryString = charSequence?.toString()?.toLowerCase() val filterResults = FilterResults() filterResults.values = if (queryString == null || queryString.isEmpty()) movies.asList() else movies.filter { it.name.toLowerCase().contains(queryString) || it.year.toString().contains(queryString) } return filterResults } } } }
Here is the layout for this adapter “item_auto_complete_text_view.xml”
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingBottom="@dimen/activity_vertical_margin"> <TextView android:id="@+id/tvMovieName" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tvMovieYear" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="5dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvMovieName" /> </androidx.constraintlayout.widget.ConstraintLayout>
Now we use this adapter.
val movieObjects = arrayOf(Movie("Avengers: Endgame", 2019), Movie("Captain Marvel", 2019), Movie("Shazam!", 2019), Movie("Spider-Man: Far From Home", 2019), Movie("Dark Phoenix", 2019), Movie("Hellboy", 2019), Movie("Glass", 2019), Movie("Reign of the Superman", 2019), Movie("Brightburn", 2019)) val adapter3 = AutoCompleteTextViewAdapter(this, R.layout.item_auto_complete_text_view, movieObjects1) //actv is the AutoCompleteTextView from your layout file actv.threshold = 1 //start searching for values after typing first character actv.setAdapter(adapter3)
See the result here:
Here you can see we have a custom layout where we have name and year below that. You also notice one thing that we now have all the result which contains string “ma” in the result. Because we use “contains()” function in our filter in the adapter.
Now we will see the listeners for AutoCompleteTextView which helps us to determine if the user selects any result from the suggestion or type any new text.
actv.setOnItemClickListener { adapterView, view, i, l -> val movie = adapterView.getItemAtPosition(i) //for adapter2 and adapter3 if (movie is Movie) { Toast.makeText(this, "Selected item: ".plus(movie.name), Toast.LENGTH_LONG).show() } //for adapter1 else if (movie is String) { Toast.makeText(this, "Selected item: ".plus(movie), Toast.LENGTH_LONG).show() } }
Here you can check if the item is selected or not from the suggestions. You can also implement TextWatcher in the AutoCompleteTextView to check if the value is edited or not.
You can find the whole code in my github repo: https://github.com/dakshbhatt21/a-computer-engineer