Display item selected from AutoCompleteTextView in Chip – Android

Display item selected from AutoCompleteTextView in Chip - Android

In this blog we are going to display a list of planets in an AutoCompleteTextView. The list of planets will popup as suggestions and when list item is clicked it will be displayed as Chip. To achieve this we are going to create an Android Application project with Empty Activity.

Create Android Application Project

Create Android application project with following properties

New Project TemplateEmpty Activity
Project NameChip Demo
Package Namecom.pcsalt.example.chipdemo
LanguageKotlin
Minimum SDKAPI 21 : 5.0 (Lollipop)
Use legacy android.support librariesUncheck

Prepare Layout Screens (activity_main.xml)

Setting up AutoCompleteTextView

The AutoCompleteTextView by itself does not provide much affect. So, we will wrap AutoCompleteTextView inside TextInputLayout and give necessary styles to look like Material Drop Down component. By doing this it will have a hint which will float to top when selected.

Note: To give drop down effect we have to provide Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu style to the InputTextLayout.
    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/til_planets"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="12dp"
        android:hint="@string/planets"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.appcompat.widget.AppCompatAutoCompleteTextView
            android:id="@+id/tiet_planets"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="text"
            android:imeOptions="actionDone"
            android:textSize="16sp" />
    </com.google.android.material.textfield.TextInputLayout>

Setting up ChipGroup

Any layout can be used as parent of Chip, but it is recommended to use ChipGroup as parent. It is because ChipGroup is capable to provide necessary features to the Chips. If we want to display Chips in a single row even if the width is full, the ChipGroup will start scrolling Horizontally. If no orientation is provided then the ChipGroup will adjust its height every time Chips inside fill the row completely. In short, if you are not sure about the amount of Chips to be placed or it is more than one, then always use ChipGroup as parent layout.

Note: Using ChipGroup as parent provides a managed container to child Chips by accommodating the chid views in Vertical Layout by default.
   <com.google.android.material.chip.ChipGroup
        android:id="@+id/cg_tags"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_marginTop="12dp"
        android:layout_marginEnd="12dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/til_planets" />

Complete Layout (activity_main.xml)

We are using ConstraintLayout as parent layout, to hold the AutoCompleteTextView and the ChipGroup. Here is the complete code of activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context=".MainActivity">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/til_planets"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="12dp"
        android:hint="@string/planets"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.appcompat.widget.AppCompatAutoCompleteTextView
            android:id="@+id/tiet_planets"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="text"
            android:imeOptions="actionDone"
            android:textSize="16sp" />
    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.chip.ChipGroup
        android:id="@+id/cg_tags"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_marginTop="12dp"
        android:layout_marginEnd="12dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/til_planets" />

</androidx.constraintlayout.widget.ConstraintLayout>

Layout for List Items (item_drop_down.xml)

To display the suggestions in the AutoCompleteTextView we can provide a custom layout. This layout will contain only one TextView which will be inflated by ArrayAdapter. Here is the layout item_drop_down.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:maxLines="1"
    android:padding="4dp"
    android:textAppearance="?attr/textAppearanceBody1"
    android:textSize="18sp" />

Enable ViewBinding

To enable ViewBinding, open the app level build.gradle i.e. app/build.gradle. In the file put the following in android {} block to enable the ViewBinding. After inserting these lines synchronise the gradle and you are good to go.

android {
    ... other blocks ...
    buildFeatures {
        viewBinding true
    }
}

Get ready with ViewBinding

Since we have enabled ViewBinding, therefore we don’t have to use findViewById() anymore. First we have to get the binding object and that binding object will provide the element reference id from layout file. To do this we have to know the binding class name. The class is autogenerated and it is named using the layout file name. Since our layout file name is activity_main.xml so, the binding class name will be ActivityMainBinding.

Benefit of using ViewBinding is that all layout elements with id will be available as binding property. The binding class will have root property to denote the root layout or the parent layout. This root should be passed into the setContentView() to render the layout file using the ViewBinding.

Note: Double check to see if you are using setContentView(binding.root) instead of setContentView(R.layout.activity_main). Else the listeners and data set using binding properties will not reflect.
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
}

Display list of planets in AutoCompleteTextView

To display a list of planets in AutoCompleteTextView we have to create a list of Strings. This list will be passed to ArrayAdapter and will be displayed as suggestions.

private val planetList = listOf(
        "Mercury",
        "Venus",
        "Earth",
        "Mars",
        "Jupiter",
        "Saturn",
        "Uranus",
        "Neptune",
        "Pluto"
)

Initialise ArrayAdapter and provide the list of planets

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    val adapter = ArrayAdapter(this@MainActivity, R.layout.item_drop_down, planetList)
    binding.tietPlanets.setAdapter(adapter)
}

Prepare to display selected item as Chip in ChipGroup

To create Chip we are going to create one method, which will take planet name as parameter and return a Chip object. In the generated Chip we are going to provide an option to close the Chip. Using this the selected planet will be removed from the ChipGroup. To do this we have to do the following

  1. Set isCloseIconVisible as true to show the close icon
  2. Provide setOnCloseIconClickListener implementation to remove the view
private fun getChip(name: String): Chip {
    return Chip(this@MainActivity).apply {
        text = name
        isCloseIconVisible = true
        setOnCloseIconClickListener {
            (it.parent as ChipGroup).removeView(it)
        }
    }
}

Add selected item to ChipGroup

Now we have an AutoCompleteTextView which is showing planet names as suggestion in drop down menu. We have to provide OnItemClickListener to allow adding Chip of selected item in ChipGroup. To do that add following code to the AutoCompleteTextView instance.

binding.tietPlanets.setOnItemClickListener { parent, _, position, _ ->
    val selectedPlanet = parent.getItemAtPosition(position) as String
    addPlanetChip(selectedPlanet)
}
private fun addPlanetChip(planetName: String) {
    selectedPlanets.add(planetName)
    binding.cgTags.addView(getChip(planetName))
}

All this implementation will display the selected items in the ChipGroup as Chip. But there is one problem, if you select any item multiple time, then it will be added as Chip that many times. So, here is complete MainActivity.kt code which will stop the duplicate items to be displayed in the ChipGroup.

Complete MainActivity.kt

To stop duplicate items to show up as Chip we are adding the selected planets to an ArrayList. And every time before adding it to list we are checking if it exists in the list or not. If it exists in the list then we are displaying a Toast message. And after removing the Chip using the close icon, we are removing the planet name from the ArrayList.

package com.pcsalt.example.chipdemo

import android.os.Bundle
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.pcsalt.example.chipdemo.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    private val planetList = listOf(
        "Mercury",
        "Venus",
        "Earth",
        "Mars",
        "Jupiter",
        "Saturn",
        "Uranus",
        "Neptune",
        "Pluto"
    )

    private val selectedPlanets = ArrayList<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val adapter = ArrayAdapter(this@MainActivity, R.layout.item_drop_down, planetList)
        binding.tietPlanets.setAdapter(adapter)

        binding.tietPlanets.setOnItemClickListener { parent, _, position, _ ->
            val selectedPlanet = parent.getItemAtPosition(position) as String
            if (selectedPlanets.contains(selectedPlanet)) {
                Toast.makeText(
                    this@MainActivity,
                    "You have already selected $selectedPlanet",
                    Toast.LENGTH_LONG
                ).show()
            } else {
                addPlanetChip(selectedPlanet)
            }
            binding.tietPlanets.setText("")
        }
    }

    private fun addPlanetChip(planetName: String) {
        selectedPlanets.add(planetName)
        binding.cgTags.addView(getChip(planetName))
    }

    private fun getChip(name: String): Chip {
        return Chip(this@MainActivity).apply {
            text = name
            isCloseIconVisible = true
            setOnCloseIconClickListener {
                selectedPlanets.remove((it as Chip).text)
                (it.parent as ChipGroup).removeView(it)
            }
        }
    }
}

Screenshots

Following are the screenshots of the application.

Points to remember

  1. To give drop down effect we have to provide Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu style to the InputTextLayout.
  2. Using ChipGroup as parent provides a managed container to child Chips by accommodating the chid views in Vertical Layout by default.
  3. Double check to see if you are using setContentView(binding.root) instead of setContentView(R.layout.activity_main). Else the listeners and data set using binding properties will not reflect.
  4. Set isCloseIconVisible as true to show the close icon
  5. Provide setOnCloseIconClickListener implementation to remove the view

Video Tutorial on YouTube

https://youtu.be/P_VzyEfpZTc

Download Source Code

The complete source code is pushed to GitHub repo. Browse it by clicking the octocat icon.

Browse source code on GitHub