Android Tag Input (Show Material Chip as Tag on Left of EditText)

Jul 7, 2020

Layout

Why use FlexboxLayout? It allows tags/chip to flow to next row, while AutoCompleteTextView / EditText will fill up the remaining space of the row.

Material Chip with EditText

Material Chip with EditText

Why not use ChipGroup? Chip cannot flow on the same row as AutoCompleteTextView / EditText within ChipGroup.

<FrameLayout 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="wrap_content"
    >
    <com.google.android.flexbox.FlexboxLayout
        android:id="@+id/tagsChipGroup"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:flexWrap="wrap"
        app:alignItems="stretch"
        app:alignContent="stretch" >

        <AutoCompleteTextView
            android:hint="Tags"
            android:id="@+id/tagsAutoCompleteTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:imeOptions="actionDone"
            android:maxLines="1"
            android:singleLine="true"
            android:inputType="text|textCapWords"
            app:layout_flexGrow="1" />

    </com.google.android.flexbox.FlexboxLayout>
</FrameLayout>

Code

val editText = tagsAutoCompleteTextView// load autocompleteval allTags = listOf("Love", "Passion", "Peace", "Hello", "Test")val adapter = ArrayAdapter<String>(context,    android.R.layout.simple_dropdown_item_1line,    allTags)editText.setAdapter(adapter)// load tagsval tags = listOf("Hello", "This Is A Big World", "Test Multiple Row")for (name in tags) {    tagsChipGroup.addChip(name) {        // TODO: handle when tag removed    }}editText.setOnAddTagListener { name ->    // TODO: handle when tag is created    val tags = tagsChipGroup.getAllChips().map { it.text.toString() }    editText.text = null}

Kotlin Extensions

fun AutoCompleteTextView.setOnAddTagListener(callback: (name: String) -> Unit) {    // select from adapter list    setOnItemClickListener { adapterView, _, position, _ ->        val name = adapterView.getItemAtPosition(position) as String        callback(name)    }    // done pressed    setOnEditorActionListener { textView, actionId, _ ->        if (actionId == EditorInfo.IME_ACTION_DONE) {            val name = textView.text.toString()            callback(name)            true        } else false    }    doAfterTextChanged { text ->        if (text != null && text.isEmpty()) {            return@doAfterTextChanged        }        // comma is detected (optional space)        if (text?.last() == ',') { //  || text?.last() == ' '            val name = text.substring(0, text.length - 1)            callback(name)        }    }}fun FlexboxLayout.getAllChips(): List<Chip> {    return (0 until childCount).mapNotNull { index ->        getChildAt(index) as? Chip    }}fun FlexboxLayout.clearChips() {    val chipViews = (0 until childCount).mapNotNull { index ->        val view = getChildAt(index)        if (view is Chip) view else null    }    chipViews.forEach { removeView(it) }}private fun ViewGroup.addChip(text: String, removeCallback: (message: String) -> Unit) {    // val chip = Chip(context)    val inflater = LayoutInflater.from(context)    val chip = inflater.inflate(R.layout.view_chip,  null) as Chip    chip.text = text    /*    test_chip.isCloseIconVisible = true    // necessary to get single selection working    test_chip.isClickable = true    test_chip.isCheckable = false     */    // val layoutParams = test_chip.layoutParams as ViewGroup.MarginLayoutParams    val layoutParams = ViewGroup.MarginLayoutParams(ViewGroup.MarginLayoutParams.WRAP_CONTENT, ViewGroup.MarginLayoutParams.WRAP_CONTENT)    layoutParams.rightMargin = 4.dpToPx(context.resources.displayMetrics)    addView(chip as View, childCount - 1, layoutParams)    chip.setOnCloseIconClickListener {        removeView(chip as View)        removeCallback(chip.text.toString())    }}

R.drawable.view_chip

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    app:closeIconEnabled="true"
     />

❤️ 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.