There are a few ways to implement Chips (Tag input)
3rd party libraries
- https://github.com/pchmn/MaterialChipsInput
- https://github.com/robertlevonyan/materialChipView
- https://github.com/splitwise/TokenAutoComplete
- https://github.com/tylersuehr7/chips-input-layout
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>