Material Design

Shape Theming

Material Design encourages brand expression through shapes. The Material Components library offers a shape library that can be used to create non-standard shapes using MaterialShapeDrawables, a Drawable that can draw custom shapes while taking shadows, elevation, scale and color into account.

On top of the shape library, the Material Components library provides a mechanism with which to easily customize component shapes across at the theme level. Shape theming offers a new dimension with which to customize the look and feel of your app.

Design & API Documentation

How it works

MaterialShapeDrawable and ShapeAppearanceModel

MaterialShapeDrawable draws itself using a path generated by a ShapeAppearanceModel. A ShapeAppearanceModel is made of CornerTreatments and EdgeTreatments that combine to create a custom shape path, and is generally passed in to a MaterialShapeDrawable’s constructor.

The shape library provides some subclassed CornerTreatments and EdgeTreatments to allow for easily building new shapes:

Both CornerTreatment and EdgeTreatment can be subclassed to create custom corners and edges.

Note: When subclassing CornerTreatment or EdgeTreatment, make sure to implement the Cloneable interface to ensure that the ShapeAppearanceModel can create deep copies of the corner and edge treatments.

Theming MaterialShapeDrawables

Components backed by MaterialShapeDrawables can be themed across an application.

Shape Theme Attributes

Theme attributes are provided to be set at the theme level to change the shape values that components read in to set their shapes.

Attribute Name Description Default Value
shapeAppearanceSmallComponent Style reference that contains shape values that are used to style small components 4dp rounded
shapeAppearanceMediumComponent Style reference that contains shape values that are used to style medium components 4dp rounded
shapeAppearanceLargeComponent Style reference that contains shape values that are used to style large components 0dp rounded

Aside from defining these attributes in your theme, you likely will not need to reference these attributes at all; the widget styles are already mapped to the appropriate theme attribute to create a seamless shape theming experience.

Usage

Building a Shape Appearance

Component shapes are backed by “shape appearances”, which are style reference that define aspects of the shape. ShapeAppearanceModel reads in the shapeAppearance style and creates corner and edge treatments out of the shapeAppearance values.

The following attributes can be used in a shapeAppearance style:

Attribute Name Format Description Supported Values
cornerFamily enum corner family to be used for all four corners rounded, cut
cornerFamilyTopLeft enum corner family to be used for the top left corner rounded, cut
cornerFamilyTopRight enum corner family to be used for the top right corner rounded, cut
cornerFamilyBottomRight enum corner family to be used for the bottom right corner rounded, cut
cornerFamilyBottomLeft enum corner family to be used for the bottom left corner rounded, cut
cornerSize dimension corner size to be used for all four corners dp values
cornerSizeTopLeft dimension corner size to be used for the top left corner dp values
cornerSizeTopRight dimension corner size to be used for the top right corner dp values
cornerSizeBottomRight dimension corner size to be used for the bottom right corner dp values
cornerSizeBottomLeft dimension corner size to be used for the bottom left corner dp values

To build a shapeAppearance, you need at least a cornerSize and cornerFamily value specified for each corner.

ShapeAppearance and shapeAppearanceOverlay attributes

Two attributes are provided to set a component’s shape style, shapeAppearance and shapeAppearanceOverlay:

Attribute Name Description
shapeAppearance Style reference that contains shape values that are used to style the component. Should point to a theme attribute style reference such as ?attr/shapeAppearanceSmallComponent.
shapeAppearanceOverlay Style reference that contains shape values that layer on top of a shapeAppearance style. This attribute is intended for overrides on top of the themed shapeAppearance shape values, and should map to a custom style reference rather than a themed style reference.

The shapeAppearanceOverlay attribute is provided to override components on a case by case basis. This attribute stacks on top of the shapeAppearance attribute; the shapeAppearance is read in first, and then if there is anything specified in the shapeAppearanceOverlay attribute, it overrides what’s set in the shapeAppearance.

Note: shapeAppearance styles require both cornerSize and cornerFamily to be set, while shapeAppearanceOverlay does not. This means that when defining custom shapeAppearance style, you should either inherit from a parent if one exists, or set both cornerSize and cornerFamily. However, when defining a style to be used as a shapeAppearanceOverlay, you should generally set an empty parent by setting parent="". This is because shapeAppearanceOverlay stacks on top of shapeAppearance, so all values will be set in the shapeAppearance.

Using shapeAppearance in the theme

The Material Components library supports theming shapes at the application level. To theme shapes across your app, specify the shape theme attributes in your theme. This will allow components that support shape theming to read in the customized values and change their shapes accordingly.

Let’s say you want to change the corners in your app to cut corners. To accomplish this, define the shape theme attributes to point to custom style references that contain shape values:

<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light">
  ...
  <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance.MyApp.SmallComponent</item>
  <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.MyApp.MediumComponent</item>
  <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.MyApp.LargeComponent</item>
  ...
</style>

The shape theme attributes should point to custom shapeAppearance styles that define both cornerSize and cornerFamily. These shapeAppearance styles might look something like this:

<style name="ShapeAppearance.MyApp.SmallComponent" parent="ShapeAppearance.MaterialComponents.SmallComponent">
  <item name="cornerFamily">cut</item>
  <item name="cornerSize">4dp</item>
</style>

<style name="ShapeAppearance.MyApp.MediumComponent" parent="ShapeAppearance.MaterialComponents.MediumComponent">
  <item name="cornerFamily">cut</item>
  <item name="cornerSize">8dp</item>
</style>

<style name="ShapeAppearance.MyApp.LargeComponent" parent="ShapeAppearance.MaterialComponents.LargeComponent">
  <item name="cornerFamily">rounded</item>
  <item name="cornerSize">4dp</item>
</style>

Material components that support shape theming read in the theme attributes and style themselves according to the themed shape values.

Customizing component shapes

Theme-wide component overrides

You can change a component’s shape across the entire app by overriding the component and defining a custom shapeAppearanceOverlay.

Let’s say you wanted to modify MaterialCardView so that it uses 16dp rounded corners acrss the entire app. All you’d have to do is define your own card style that extends from the widget’s style, and set the relevant attributes to the desired theme attributes:

<style name="Widget.MyApp.MaterialCardView" parent="Widget.MaterialComponents.MaterialCardView">
  <item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.MyApp.MaterialCardView</item>
</style>

And define ShapeAppearanceOverlay.MyApp.MaterialCardView as follows:

<style name="ShapeAppearanceOverlay.MyApp.MaterialCardView">
  <item name="cornerFamily">rounded</item>
  <item name="cornerSize">16dp</item>
</style>

Then make sure to define the custom component style in your theme:

<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light">
  ...
  <item name="materialCardViewStyle">@style/Widget.MyApp.MaterialCardView</item>
  ...
</style>

All cards in your app should now have 16dp rounded corners.

Individual component overrides

You can also change an individual component’s shape on a case by case basis. Let’s say cards in your theme have 16dp rounded corners, but there’s one card that should have 16dp cut corners. To change that card’s cornerFamily, you can set the shapeAppearanceOverlay attribute on the card in your layout.

Define a custom shapeAppearanceOverlay style with just the attribute you want to overlay on top of the existing shapeAppearance:

<style name="ShapeAppearanceOverlay.MyApp.MaterialCardView.Rounded" parent="">
  <item name="cornerFamily">rounded</item>
</style>

Then, set the card’s shapeAppearanceOverlay attribute to the ShapeAppearanceOverlay style in your layout:

<com.google.android.material.card.MaterialCardView
    style="@style/Widget.MyApp.MaterialCardView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="@dimen/mtrl_card_spacing"
    android:layout_marginTop="@dimen/mtrl_card_spacing"
    android:layout_marginRight="@dimen/mtrl_card_spacing"
    app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MyApp.MaterialCardView">
  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/card_text"/>
</com.google.android.material.card.MaterialCardView>

The card should now have 16dp rounded corners.

Supported Components

The following is a list of Material components that support shape theming. Components that support shape theming have a shapeAppearance attribute, a shapeAppearanceOverlay attribute, and are backed by a MaterialShapeDrawable.