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 Template | Empty Activity |
---|---|
Project Name | Chip Demo |
Package Name | com.pcsalt.example.chipdemo |
Language | Kotlin |
Minimum SDK | API 21 : 5.0 (Lollipop) |
Use legacy android.support libraries | Uncheck |
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 Chip
s. If we want to display Chip
s 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 Chip
s inside fill the row completely. In short, if you are not sure about the amount of Chip
s 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
- Set isCloseIconVisible as true to show the close icon
- 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
- To give drop down effect we have to provide
Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu
style to the InputTextLayout. - Using
ChipGroup
as parent provides a managed container to child Chips by accommodating the chid views in Vertical Layout by default. - 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. - Set
isCloseIconVisible
as true to show the close icon - Provide
setOnCloseIconClickListener
implementation to remove the view
Video Tutorial on YouTube
Download Source Code
The complete source code is pushed to GitHub repo. Browse it by clicking the octocat icon.