Beschreibendes Bild für Seite: Android TextInputLayout mit Spinner-Design

Android TextInputLayout mit Spinner-Design

Manchmal besteht die Notwendigkeit, ein festgelegtes Set an Optionen für ein Eingabefeld in Ihrem Formular bereitzustellen. Um das zu erreichen, verwenden wir in Android normalerweise einen Spinner.

Dieser Ansatz hat jedoch einen Nachteil: Der Spinner ist optisch nicht ansprechend und passt auch nicht gut zu den TextInputLayouts aus der Material Design Bibliothek.

Der aktuelle System-Spinner sieht wie folgt aus:

Spinner android guides

Android developer guide on Spinner

Was schlägt Google vor? Google veröffentlicht derzeit fast monatlich Updates zu den Material Design Richtlinien. Derzeit finden wir folgende Grafik auf der Seite:

Material text inputs

Material.io - Text Fields

Wir sehen ein TextInputLayout mit einem Dropdown-Symbol, das genau so aussieht, wie das, was wir erreichen wollen. Aber wo ist die Implementierung? Leider gibt es keine offizielle Implementierung - Google stellt keine standard Implementierung für ein Spinner-Verhalten zur Verfügung.

Also müssen wir es selbst bauen!

1. Dropdown-Element erstellen:

Wir benötigen ein Layout für die Elemente, die in der Auswahlliste erscheinen sollen.

hier eine Implementierung, die die Materialrichtlinien respektiert:

<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. TextInputLayout mit AutoCompleteTextView

Nachdem wir das Dropdown-Element erstellt haben, können wir ein TextInputLayout zu unserer View hinzufügen. Wir verschachteln eine AutoCompleteTextView darin, welche das richtige Verhalten für unser Dropdown erzwingen kann.

<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>

Zusätzlich wenden wir einen Stil unserer Wahl aus der Materialbibliothek auf das TextInputLayout an und setzen dann folgende Attribute:

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

Diese Attribute müssen gesetzt werden, um zu verhindern, dass die Tastatur geöffnet wird, und führen dazu, dass nur die Autofill-Optionen angezeigt werden, die unser Dropdown-Menü darstellen soll.

3. Adapter ohne Filter erstellen

Um Fehler zu vermeiden, die durch die Filterfunktionalität im ArrayAdapter verursacht werden, müssen wir unsere eigene Implementierung erstellen.

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()
        }
    }

}

Für den Filter erstellen wir FilterResults, die immer alle unsere Elemente enthalten.

4. Alles zusammenbringen

Als letzten Schritt müssen wir unseren Adapter auf die View setzen und das Layout, das wir in Schritt 1. für unsere Elemente erstellt haben, bereitstellen.

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

5. Nachteile

Auch wenn dieser Ansatz funktioniert, müssen wir einige manuelle Schritte tätigen, um tatsächlich unser Ergebnis zu erhalten. Auch die Ripple Tap-Animation ist manchmal etwas Fehlerhaft.

6. Eine letzte Sache

Wir könnten aus all diesen Codeschnipseln eine custom View erstellen, um die Benutzerfreundlichkeit unseres Codes zu verbessern und es einfacher zu machen, mehrere Dropdowns in unserer Anwendung zu implementieren. Eine weitere, letzte, Überlegung wäre, eine Validierung zu implementieren, so dass man keine Werte eingeben kann, die vom Dropdown nicht unterstützt werden.

Mehr Beiträge