Hands-on with Material Components for Android: Chips

Part 5 of a series covering practical usage of Material Components for Android

Nick Rout
Over Engineering

--

Featured in Android Weekly Issue #362

This post will be covering the features and API of Chip components. To find out how to handle initial setup of Material Components for Android (including the Gradle dependency and creating an app theme), please see my original post:

Chips are compact components that display discrete information. Given the simplicity of their makeup and small size, they are flexible enough to be used for entering information, filtering content, selection and triggering actions. Chips should be grouped accordingly and are rarely used as standalone elements.

From a design perspective, there are four main types of chips which can be used in different scenarios:

  • Input chips: Represent discrete information (an entity, attribute, etc.) as input to a field. Characterized by the close icon which is used to remove them from a group.
  • Choice chips: Represent a single choice selection in a group of two or more chips. Characterized by the different checked/unchecked colors, toggled via a tap.
  • Filter chips: Represent a multiple choice filter in a group of two or more chips. Characterized by the checked icon, toggled via a tap.
  • Action chips: Simply used to trigger a given action. Contains standard icon and text label.

Basic usage 🏁

A Chip can be included in your layout like so:

<FrameLayout
...
>

<com.google.android.material.chip.Chip
android:id="@+id/chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Chip"
/>

</FrameLayout>

Choosing a style 🤔

As discussed in the intro section above, a variety of chip types exist. These types map to styles that you can apply to a Chip. The full list of styles and their attributes can be found on GitHub. These style variants inherit from Widget.MaterialComponents.Chip, each with an optional style suffix:

  • Input chip: *.Entry (standalone default)
  • Choice chip: *.Choice
  • Filter chip: *.Filter
  • Action chip: *.Action (default)

Applying attributes 🎛️

A number of attributes exist to customize the appearance and behavior of Chips. The defaults for these attributes vary depending on the style of Chip used. A large amount of attributes exist and not all of them are listed here (eg. Icon padding). Some attributes are purposefully left out and have been reserved for the “Theming” section below. The full list of attributes can be found on GitHub.

Chip icon

  • chipIcon: An icon Drawable that is displayed at the start of the Chip.
  • chipIconVisible: Whether or not the chip icon is visible.
  • chipIconSize: The size of the icon.

Close icon

  • closeIcon: A clickable close icon Drawable that is displayed at the end of the Chip.
  • closeIconVisible: Whether or not the close icon is visible.
  • closeIconSize: The size of the close icon.

Checked icon

  • checkedIcon: A check icon Drawable that is displayed at the start of the Chip and is toggled via tapping the Chip.
  • checkedIconVisible: Whether or not the checked icon is visible.
  • android:checkable: Whether or not checkable tapping is enabled.

Listening for clicks, closes and checks 👂

Chips have a few UI elements that can respond to clicks. If the android:checkable attribute is enabled, clicks on the chip itself will toggle the checked/unchecked state. All of these events can be observed with callbacks.

Listening for a click on the Chip itself is done like so:

chip.setOnClickListener {
// Handle chip click
}

We can also listen for a click on the Chip close icon:

chip.setOnCloseIconClickListener {
// Handle chip close icon click
}

Finally, we can listen for checked/unchecked state changes:

chip.setOnCheckedChangeListener { chip, isChecked ->
// Handle chip checked/unchecked
}

Grouping Chips 👨‍👩‍👧‍👦

As mentioned above in the intro section, Chips are most commonly used in groups. Strictly speaking, any ViewGroup can be used to achieve this (eg. a RecyclerView). That being said, the ChipGroup class exists to conveniently handle certain multi-Chip layout and behavior patterns. Specifically, this includes reflowing Chips across multiple lines and handling single selection.

Chips grouped with ChipGroup

Chips can be grouped with a ChipGroup like so:

<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>

<com.google.android.material.chip.Chip
android:id="@+id/choice1"
...
/>

<com.google.android.material.chip.Chip
android:id="@+id/choice2"
...
/>

...

</com.google.android.material.chip.ChipGroup>

If a single horizontal line of Chips is preferable over multi-line reflow, you would implement this like so:

<!-- Optionally wrap ChipGroup in a HorizontalScrollView for scrolling behavior --><com.google.android.material.chip.ChipGroup
...
app:singleLine="true"
>
...</com.google.android.material.chip.ChipGroup>
ChipGroup with singleLine enabled

The spacing between grouped Chips (which defaults to 8dp) can be adjusted with the chipSpacing, chipSpacingHorizontal and chipSpacingVertical attributes:

Vertical and horizontal Chip spacing

The singleSelection attribute can be set to true on a ChipGroup in order to toggle single-select and multi-select behavior of child Chips.

The selectionRequired attribute can be set to true on a ChipGroup to prevent all child Chips from being deselected (i.e. At least one option should be chosen).

Single- vs. multi-select

Lastly, a number of APIs exist for programmatically setting, getting and listening for changes to child Chip checked/unchecked state:

chipGroup.check(R.id.choice1)
val checkedChipId = chipGroup.checkedChipId // Will return View.NO_ID if singleSelection = false
val checkedChipIds = chipGroup.checkedChipIds
chipGroup.setOnCheckedChangeListener { group, checkedId ->
// Handle child Chip checked/unchecked
}

Advanced usage with ChipDrawable 🤓

In certain cases, you may wish to use a standalone chip where a Drawable is required instead of a View. For this, we can use the ChipDrawable class.

Firstly, an XML representation of the chip needs to be added to your res/xml folder, as a separate file, using the <chip> tag:

<chip
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:chipIcon="@drawable/ic_chip"
app:closeIconVisible="false"
android:text="ChipDrawable"
/>

All of the non-interactive styleable attributes from the Chip View are supported. A ChipDrawable can then be inflated from this resource like so:

val chipDrawable = ChipDrawable.createFromResource(context, R.xml.chip)

As an example, consider an editable e-mail address field that converts addresses to chips as they are typed and validated. We can combine ChipDrawable with spans to add a chip to an EditText:

chip.setBounds(0, 0, chip.intrinsicWidth, chip.intrinsicHeight)
val span = ImageSpan(chip)
val text = editText.text!!
text.setSpan(span, 0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
A ChipDrawable used for an EditText span

Theming 🎨

Chips can be themed in terms of the three Material Theming subsystems: color, typography and shape. We have already shown which styles to use in the “Choosing a style” section above. When implementing global custom Chip, ChipDrawable and ChipGroup styles, reference them in your app theme with the chipStyle, chipStandaloneStyle and chipGroupStyle attributes respectively.

Color

The color of the Chip background can be customized with the chipBackgroundColor attribute. This requires a ColorStateList, meaning a <selector> for checked/enabled/disabled states is required. It defaults to colorPrimary(checked)/colorOnSurface(unchecked) for choice chips and colorOnSurface for all other types, with different opacities per state.

The color of the text label can be customized with the android:textColorattribute. This too requires a ColorStateList. It defaults to colorPrimary(checked)/colorOnSurface(unchecked) for choice chips and colorOnSurface for all other types, with different opacities per state.

The color of the Chip icons can be customized with the chipIconTint and closeIconTint attributes. There is no attribute for the checked icon, so the tint should be embedded in the resource. These too require ColorStateLists. The default for chipIconTint is no tint while closeIconTint defaults to colorOnSurface.

Chips can have an optional border stroke and the chipStrokeColor attribute can be used for this. This defaults to colorOnSurface. Seeing as the chipStrokeWidth attribute defaults to 0dp, this stroke is not visible by default.

Lastly, the color of the chip touch ripple can be customized with the rippleColor attribute. It too accepts a ColorStateList and defaults to colorPrimary(pressed)/colorOnSurface(focused) for choice chips and colorOnSurface for all other types, with different opacities per state.

Color theming

Typography

At the time of writing, the latest release of Material Components for Android is 1.2.0-alpha06 and global type theming attributes (eg. fontFamily) do not affect Chip. You can star the issue for this on the issue tracker.

To achieve this, we need to specify a custom text appearance style for chips:

<style name="ChipTextAppearance" parent="TextAppearance.MaterialComponents.Chip">
<item name="fontFamily">@font/roboto_mono</item>
<item name="android:fontFamily">@font/roboto_mono</item>
</style>

We could apply this directly to a chip or in an individual chip style by referencing it with the android:textAppearance attribute.

Type theming

Shape

The shape of a chip background can be customized with the shapeAppearance attribute. This defaults to shapeAppearanceSmallComponent.

Shape theming

More resources 📚

I hope this post has provided some insight into Chips and how they can be used in your Android app(s). If you have any questions, thoughts or suggestions then I’d love to hear from you!

Find me on Twitter @ricknout

--

--

Nick Rout
Over Engineering

Principal Android Engineer at GoDaddy | Ex-Google | Google Developer Expert for Android