Android Tag Input/EditText (Material Chips)

Tag input like StackOverflow or to contact/email like Gmail

There are a few ways to implement Chips (Tag input)

3rd party libraries

Or you could use Material Chips, which is more flexible but require more work.

This tutorial will use Material Chips.

The following example use AutoCompleteTextView

  • Allow multiple unique tags
  • Allow user to either select from a drop list or add a new tag by text
  • Enter their custom tag and hit enter/done on the keyboard to add tag
  • Enter their custom tag and hit comma or space to add tag
  • The added tags are shown below AutoCompleteTextView
  • Allow tags to be removed

Layout

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <AutoCompleteTextView
        android:id="@+id/mainTagAutoCompleteTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textCapWords"
        android:completionThreshold="1"
        android:imeOptions="actionDone"
        />

    <com.google.android.material.chip.ChipGroup
        android:id="@+id/mainTagChipGroup"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:chipSpacingVertical="4dp"
        />

</LinearLayout>

NOTE: You can show ChipGroup (left) and AutoCompleteTextView (right) horizontally. Remove underline from AutoCompleteTextView, put a fake underline (optional) below ChipGroup and AutoCompleteTextView and set minimum length to AutoCompleteTextView.

val allTags = listOf("Love", "Passion", "Peace", "Hello", "Test")// use ViewModel to maintain ui state - internal lateinit var mainTags: MutableList<String>viewModel.mainTags = item.mainTags?.toMutableList() ?: mutableListOf("Hello")loadTagsUi(mainTagAutoCompleteTextView, mainTagChipGroup, viewModel.mainTags, allTags)
private fun loadTagsUi(autoCompleteTextView: AutoCompleteTextView,                       chipGroup: ChipGroup,                       currentTags: MutableList<String>,                       allTags: List<String>) {    val adapter = ArrayAdapter<String>(this,        android.R.layout.simple_dropdown_item_1line,        allTags)    autoCompleteTextView.setAdapter(adapter)    fun addTag(name: String) {        if (!name.isEmpty() && !currentTags.contains(name)) {            addChipToGroup(name, chipGroup, currentTags)            currentTags.add(name)        }        else {            Timber.d("Invalid tag: $name")        }    }    // select from auto complete    autoCompleteTextView.setOnItemClickListener { adapterView, _, position, _ ->        autoCompleteTextView.text = null        val name = adapterView.getItemAtPosition(position) as String        addTag(name)    }    // done keyboard button is pressed    autoCompleteTextView.setOnEditorActionListener { textView, actionId, keyEvent ->        if (actionId == EditorInfo.IME_ACTION_DONE) {            val name = textView.text.toString()            textView.text = null            addTag(name)            return@setOnEditorActionListener true        }        false    }    // space or comma is detected    autoCompleteTextView.addTextChangedListener {        if (it != null && it.isEmpty()) {            return@addTextChangedListener        }        if (it?.last() == ',' || it?.last() == ' ' ) {            val name = it.substring(0, it.length - 1)            addTag(name)            mainTagAutoCompleteTextView.text = null            // mainTagAutoCompleteTextView.removeTextChangedListener(this)        }    }    // initialize    for (tag in currentTags) {        addChipToGroup(tag,  mainTagChipGroup, currentTags)    }}private fun addChipToGroup(name: String, chipGroup: ChipGroup, items: MutableList<String>) {    val chip = Chip(context)    chip.text = name    chip.isClickable = true    chip.isCheckable = false    chip.isCloseIconVisible = true    chipGroup.addView(chip)    chip.setOnCloseIconClickListener {        chipGroup.removeView(chip)        items.remove(name)    }}

You might bump into java.lang.IllegalArgumentException: The style on this component requires your app theme to be Theme.MaterialComponents (or a descendant) error.

Edit res/values/styles and make sure AppTheme parent inherit from Theme.MaterialComponents.

<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.