Descriptive image for Page: TextInputLayout as Spinner

TextInputLayout as Spinner

Sometimes there is the need to provide a fixed set of options for an input field in your form. To accomplish that we usually would use a Spinner in Android.

This approach has a drawback: The Spinner is not visually appealing and also does not go well with the TextInputLayouts from the Material Design library.

The current system Spinner looks like this:

Spinner android guides

Android developer guide on Spinner

What does Google suggest? Google is pushing updates to the Material Design guidelines almost monthly these days. Currently we can find following graphic on the page:

Material text inputs

Material.io - Text Fields

We can see a TextInputLayout with a dropdown icon, which looks exactly like the thing we want to accomplish. But where is the implementation? Sadly there is no official one – Google does not provide a simple implementation for a Spinner behavior.

So let’s build it on our own!

1. Create dropdown item:

We need a layout for the items that will appear in the dropdown.

Implementation that respects the Material Guidelines:

<TextView xmlns:android = "http://schemas.android.com/apk/res/android"
	android:layout_width = "match_parent"
	android:layout_height = "wrap_content"
	android:ellipsize = "end"
	android:maxLines = "1"
	android:padding = "16dp"
	android:textAppearance = "?attr/textAppearanceSubtitle1" />

2. Create TextInputLayout with AutoCompleteTextView

After creating the dropdown item we can go on and add a TextInputlayout to our view. We nest an AutoCompleteTextView inside, which will force the correct behavior for our dropdown.

<com.google.android.material.textfield.TextInputLayout
   style = "@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"
   android:layout_width = "0dp"
   android:layout_height = "wrap_content"
   android:hint = "@string/dashboard_til_working_hours_total_unit_hint">

   <AutoCompleteTextView android:id = "@+id/tv_with_spinner_behavior"
      android:layout_width = "match_parent"
      android:layout_height = "match_parent"
      android:clickable = "true"
      android:focusable = "true"
      android:focusableInTouchMode = "false"
      android:inputType = "none"/>

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

Additionally we apply a style of our liking from the Material library to the TextInputLayout and then set following attributes:

  • inputType = “none”
  • clickable = “false”
  • focusable = “true”
  • focusableInTouchMode = “false”

These need to be set to prevent the keyboard from opening and will result in only showing the Autofill options, that represent our dropdown menu.

3. Create adapter without filter

To prevent bugs caused by the filter functionality in ArrayAdapter, we need to create our own implementation.

class NoFilterAdapter(
    @NonNull context: Context,
    @LayoutRes resource: Int,
    @NonNull val items: Array
) : ArrayAdapter(context, resource, items) {

    override fun getFilter(): Filter {
        return DisabledFilter()
    }

    private inner class DisabledFilter : Filter() {
        override fun performFiltering(arg0: CharSequence): FilterResults {
            val result = FilterResults()
            result.values = items
            result.count = items.size
            return result
        }
        override fun publishResults(arg0: CharSequence, arg1: FilterResults) {
            notifyDataSetChanged()
        }
    }

}

For the filter we create FilterResults that always contain all of our items.

4. Bring everything together

As the last step we need to set our adapter on the view and provide the layout we created in step 1. for our items.

tvWithSpinnerBehavior.setAdapter(
    NoFilterAdapter<String>(
        requireContext(),
        R.layout.drop_down_item,
        resources.getStringArray(R.array.time_unit)
    )
)

5. Drawbacks

Even though this approach works well from a UI perspective, we need to do some manual work to actual get our result. Also the tap animation ripple is somewhat behaving not so well sometimes.

6. One last thing

We could create a custom view out of all this code snippets to improve the usability of our code and make it easier to implement multiple dropdowns within our app. Another additional thing to think about would be implementing some validation, so you cannot set values, which are not supported by the dropdown.

More posts