- Playing with elevation in Android
- What is that shadow all about
- Elevation tweaks in action
- One last thing before you go
- Elevation & shadows
- Elevation
- Resting elevation
- Dynamic elevation offsets
- Elevation (Android) Expand and collapse content An arrow that points down when collapsed and points up when expanded.
- Resting elevation
- Responsive elevation and dynamic elevation offsets
- Avoiding elevation interference
- Android material button elevation
Playing with elevation in Android
Everyone knows that Material Design has shadows. Most know that you can control the virtual Z coordinate of Material elements in Android by using the elevation property, to control the shadow. Very few know that there’s so much more you can do to tweak the shadows your UI elements cast!
Update 6th Nov 2018: I just published a follow-up to this article with new APIs added in P, and a bunch of other goodies. Do check it out!
What is that shadow all about
In Material Design, the elevation is a manifestation of the virtual Z coordinate of a material plane relative to the screen’s “base” plane. For example:
Image shamelessly ripped from the Material Design guidelines.
In the Material Design system, there are two light sources. One is a key light that sits above the top of the screen, and an ambient light that sits directly above the centre of the screen:
Image shamelessly ripped from the Touchlab blog.
These two lights cast each their own shadow, one which is mostly affecting the bottom edge of a material sheet (key light), and the other which is affecting all edges (ambient light):
Image derived from the Material Design guidelines.
The elevation property directly controls the shape of the resulting shadow; you can see this clearly with buttons, which change their elevation based on their state:
Image from Vadim Gromov’s Dribbble.
You may think that the elevation property is the only way to control how shadows look, but that’s not true.
In Android, there is a very little known API called Outline that is providing the required information for a Material sheet to project a shadow. The default behaviour for View s is to delegate the outline definition to their background drawable. ShapeDrawable s for example provide outlines that match their shapes, while ColorDrawable s, BitmapDrawable s, etc. provide a rectangle matching their bounds. But nothing says we cannot change that, and tell a view to use a different ViewOutlineProvider , using the setOutlineProvider() method:
If we control the ViewOutlineProvider , we can tweak the resulting Outline , tricking the OS into drawing whatever shadow we want:
You can use elevation and Outline to do all sorts of tweaks to the shape and position of an elevation shadow:
Believe it or not, I have actually captured this one myself on my phone.
You will notice how the shadow here does not just adapt to different elevation values, but is also translated around and gets a larger or smaller size than the view itself.
Another thing you can do is to assign a shape that is different from the actual outline of the view itself — I cannot think of any situation in which this would make sense, but you could. The only limitation is that the shape must be convex. There are convenience methods on Outline to have ellipses, rectangles and rounded rectangles, but you could also use any arbitrary Path , as long as it’s convex.
Unfortunately it’s not possible to exaggerate some effects too much, since as you can see there are some shortcuts the system takes when rendering the shadows which will create some rather annoying artefacts when you hit them.
In case you are curious how shadows are rendered in Android, the relevant code is in the hwui package in AOSP — you can start looking at AmbientShadow.cpp (note: starting with Android 10, hwui is not really used anymore, and everything is rendered by Skia instead).
Another limitation is that we cannot tint the elevation shadow, we’re stuck with the default grey, but to be honest I don’t believe that’s a bad thing 😉
Elevation tweaks in action
I’ve used this technique to come up with a peculiar elevation appearance for the cards in Squanchy, an open source conference app I’ve been working on in the last year:
As you can see, the cards have a shadow that looks way more diffuse than the regular elevation shadows. This is obtained by having an Outline that is 4dp smaller than the card, and an elevation of 4dp :
The cards have an android:stateListAnimator that also tweaks their elevation and translationZ based on their pressed state, like Material buttons do. You can see how the cardInset* attributes are then used in the CardLayout code to shrink the Outline that we provide to the system.
When you scroll the schedule in Squanchy, you might notice that the shadow tends to change size as a card scrolls along the Y axis:
If the effect is too subtle in a gif for you to see, this image makes it crystal clear:
How is that possible? We definitely don’t change the elevation and outline based on the y-position of an item (we could, but it’s really not a good idea as it requires recalculating outlines on each scroll event).
You’ll remember I mentioned earlier how there are two shadows in the Material Design environment, one which sits above the top of the screen, and one that sits directly above the centre. Well, the top light — that is the key light — is casting a longer shadow when an item gets farther away from it. This is actually something that is always true in Android, you just don’t notice it as much in normal circumstances. The Squanchy style makes it more obvious though, and you can even exaggerate it further by using a higher elevation value:
One last thing before you go
Lastly, remember that Outlines aren’t just used for shadows, but by default they define the clipping of the view too! If you have a weird outline and don’t want it to influence the drawing of your actual view, you’ll want to call setClipToOutline(false) on it to avoid nasty surprises.
This is only important when the Outline you provide has canClip() returning true , which is the case when the outline is a rectangle, a rounded rectangle, or a circle. Non-round ovals, and arbitrary paths, are not able to provide clipping, so setClipToOutline() has no effect in those cases.
Fun fact: rectangles and circles are all internally represented as special cases of rounded rectangles. A rectangle is a rounded rectangle with a corner radius of zero, and a circle is a rounded rectangle whose corner radius is equal to half the circle height/width.
If you want to read some more on the topic, the Android Developers website has a page on Defining shadows and clipping views, that goes through the same topics with code examples, and links to some more javadocs.
To play around with elevations on your own Android device, I have cooked up a simple playground app:
The code for the playground app is open source on GitHub.
Источник
Elevation & shadows
In the physical world, objects can be stacked or affixed to one another, but cannot pass through each other. Objects also cast shadows and reflect light.
Material Design reflects these qualities to form a spatial model that is familiar to users and can be applied consistently across apps.
Elevation
Measured from the front of one surface to the front of another, an element’s elevation indicates the distance between surfaces and the depth of its shadow.
Resting elevation
All material elements have resting elevations. While components have consistent resting elevations across apps, they may have different resting elevations across platforms and devices.
Dynamic elevation offsets
Dynamic elevation offsets are the goal elevation that a component moves towards, relative to its resting state.
Elevation (Android) Expand and collapse content An arrow that points down when collapsed and points up when expanded.
Elevation is the relative depth, or distance, between two surfaces along the z-axis.
Specifications:
- Elevation is measured in the same units as the x and y axes, typically in density-independent pixels (dp). Because material elements have depth (all material is 1dp thick), elevation is measured in distance from the top of one surface to the top of another.
- A child object’s elevation is relative to the parent object’s elevation.
The images and values shown are for Android apps.
Multiple elevation measurements for two objects
Resting elevation
All material objects, regardless of size, have a resting elevation, or default elevation that does not change. If an object changes elevation, it should return to its resting elevation as soon as possible.
Desktop resting elevation is 2dp below the listed values to accommodate mouse and non-touch environments.
Component elevations:
- Components maintain consistent resting elevations across apps. For example, the floating action button’s elevation does not vary from one app to another.
- Components may have different resting elevations across platforms and devices, depending on the depth of the environment. For instance, TV has a greater depth than desktop as it has a larger screen and is viewed from further away. Similarly, both TV and desktop have a greater depth than mobile.
Responsive elevation and dynamic elevation offsets
Some component types have responsive elevation, meaning they change elevation in response to user input (e.g., normal, focused, and pressed) or system events. These elevation changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative to the component’s resting state. They ensure that elevation changes are consistent across actions and component types. For example, all components that lift on press have the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting elevation.
Avoiding elevation interference
Components with responsive elevations may encounter other components as they move between their resting elevations and dynamic elevation offsets. Because material cannot pass through other material, components avoid interfering with one another any number of ways, whether on a per-component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference. For example, a floating action button (FAB) can disappear or move off-screen before a user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference. For example, position the FAB to one side of a stream of cards so the FAB won’t interfere when a user tries to pick up one of cards.
Источник
Android material button elevation
Buttons allow users to take actions, and make choices, with a single tap.
Contents
Before you can use Material buttons, you need to add a dependency to the Material Components for Android library. For more information, go to the Getting started page.
Note: is auto-inflated as via MaterialComponentsViewInflater when using a Theme.Material3.* theme.
Making buttons accessible
Buttons support content labeling for accessibility and are readable by most screen readers, such as TalkBack. Text rendered in buttons is automatically provided to accessibility services. Additional content labels are usually unnecessary.
For more information on content labels, go to the Android accessibility help guide.
Toggle button is an additional pattern using a segmented container or icon.
Elevated buttons are essentially outlined buttons with a shadow. To prevent shadow creep, only use them when absolutely necessary, such as when the button requires visual separation from a patterned background.
Elevated button examples
API and source code:
The following example shows an elevated button with a text label.
Adding an icon to an elevated button
The following example shows an elevated button with an icon.
Anatomy and key properties
An elevated button has a text label, a stroked container and an optional icon.
Text label attributes
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Text label | android:text | setText getText | null |
Color | android:textColor | setTextColor getTextColor | ?attr/colorOnSurface (see all states) |
Typography | android:textAppearance | setTextAppearance | ?attr/textAppearanceLabelLarge |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Color | app:backgroundTint | setBackgroundColor setBackgroundTintList getBackgroundTintList | ?attr/colorSurface (see all states) |
Stroke color | app:strokeColor | setStrokeColor setStrokeColorResource getStrokeColor | null |
Stroke width | app:strokeWidth | setStrokeWidth setStrokeWidthResource getStrokeWidth | 0dp |
Shape | app:shapeAppearance | setShapeAppearanceModel getShapeAppearanceModel | ?attr/shapeAppearanceSmallComponent |
Elevation | app:elevation | setElevation getElevation | 1dp |
Ripple color | app:rippleColor | setRippleColor setRippleColorResource getRippleColor | ?attr/colorOnSurface at 16% opacity (see all states) |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Icon | app:icon | setIcon setIconResource getIcon | null |
Color | app:iconTint | setIconTint setIconTintResource getIconTint | ?attr/colorOnSurface (see all states) |
Size | app:iconSize | setIconSize getIconSize | wrap_content |
Gravity (position relative to text label) | app:iconGravity | setIconGravity getIconGravity | start |
Padding (space between icon and text label) | app:iconPadding | setIconPadding getIconPadding | 8dp |
Element | Style |
---|---|
Default style | Widget.Material3.Button.ElevatedButton |
Icon style | Widget.Material3.Button.ElevatedButton.Icon |
See the full list of styles and attrs.
Filled button’s contrasting surface color makes it the most prominent button after the FAB. It’s used for final or unblocking actions in a flow.
Note The filled button is the default style if the style is not set.
Filled button examples
API and source code:
The following example shows a filled button with a text label and a filled container.
Note: Since this is the default type, you don’t need to specify a style tag as long as you are using a Material Components Theme. If not, set the style to @style/Widget.Material3.Button .
Adding an icon to a filled button
The following example shows a filled button with an icon.
Anatomy and key properties
A filled button has a text label, a filled container and an optional icon.
Text label attributes
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Text label | android:text | setText getText | null |
Color | android:textColor | setTextColor getTextColor | ?attr/colorOnPrimary (see all states) |
Typography | android:textAppearance | setTextAppearance | ?attr/textAppearanceLabelLarge |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Color | app:backgroundTint | setBackgroundColor setBackgroundTintList getBackgroundTintList | ?attr/colorPrimary (see all states) |
Stroke color | app:strokeColor | setStrokeColor setStrokeColorResource getStrokeColor | null |
Stroke width | app:strokeWidth | setStrokeWidth setStrokeWidthResource getStrokeWidth | 0dp |
Shape | app:shapeAppearance | setShapeAppearanceModel getShapeAppearanceModel | ?attr/shapeAppearanceSmallComponent |
Elevation | app:elevation | setElevation getElevation | 2dp |
Ripple color | app:rippleColor | setRippleColor setRippleColorResource getRippleColor | ?attr/colorOnPrimary at 16% opacity (see all states) |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Icon | app:icon | setIcon setIconResource getIcon | null |
Color | app:iconTint | setIconTint setIconTintResource getIconTint | ?attr/colorOnPrimary (see all states) |
Size | app:iconSize | setIconSize getIconSize | wrap_content |
Gravity (position relative to text label) | app:iconGravity | setIconGravity getIconGravity | start |
Padding (space between icon and text label) | app:iconPadding | setIconPadding getIconPadding | 8dp |
Element | Style |
---|---|
Default style | Widget.Material3.Button |
Icon style | Widget.Material3.Button.Icon |
Unelevated style | Widget.Material3.Button.UnelevatedButton |
Unelevated icon style | Widget.Material3.Button.UnelevatedButton.Icon |
Default style theme attribute: ?attr/materialButtonStyle
See the full list of styles and attrs.
Filled tonal button
Filled tonal buttons have a lighter background color and darker label color, making them less visually prominent than a regular filled button. They’re still used for final or unblocking actions in a flow, but may be better when these actions don’t require quite so much emphasis.
Filled tonal button examples
API and source code:
The following example shows a filled tonal button with a text label and a filled container.
Adding an icon to a filled tonal button
The following example shows a filled tonal button with an icon.
Anatomy and key properties
A filled tonal button has a text label, a filled container and an optional icon.
Text label attributes
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Text label | android:text | setText getText | null |
Color | android:textColor | setTextColor getTextColor | ?attr/colorOnSecondaryContainer (see all states) |
Typography | android:textAppearance | setTextAppearance | ?attr/textAppearanceLabelLarge |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Color | app:backgroundTint | setBackgroundColor setBackgroundTintList getBackgroundTintList | ?attr/colorSecondaryContainer (see all states) |
Stroke color | app:strokeColor | setStrokeColor setStrokeColorResource getStrokeColor | null |
Stroke width | app:strokeWidth | setStrokeWidth setStrokeWidthResource getStrokeWidth | 0dp |
Shape | app:shapeAppearance | setShapeAppearanceModel getShapeAppearanceModel | ?attr/shapeAppearanceSmallComponent |
Elevation | app:elevation | setElevation getElevation | 2dp |
Ripple color | app:rippleColor | setRippleColor setRippleColorResource getRippleColor | ?attr/colorOnSecondaryContainer at 16% opacity (see all states) |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Icon | app:icon | setIcon setIconResource getIcon | null |
Color | app:iconTint | setIconTint setIconTintResource getIconTint | ?attr/colorOnSecondaryContainer (see all states) |
Size | app:iconSize | setIconSize getIconSize | wrap_content |
Gravity (position relative to text label) | app:iconGravity | setIconGravity getIconGravity | start |
Padding (space between icon and text label) | app:iconPadding | setIconPadding getIconPadding | 8dp |
Element | Style |
---|---|
Default style | Widget.Material3.Button.TonalButton |
Icon style | Widget.Material3.Button.TonalButton.Icon |
See the full list of styles and attrs.
Outlined buttons are for actions that need attention but aren’t the primary action, such as “See all” or “Add to cart.” This is also the button used to give someone the opportunity to change their mind or escape a flow.
Outlined button examples
API and source code:
The following example shows an outlined button with a text label and stroked container.
Adding an icon to an outlined button
The following example shows an outlined button with an icon.
Anatomy and key properties
An outlined button has a text label, a stroked container and an optional icon.
Text label attributes
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Text label | android:text | setText getText | null |
Color | android:textColor | setTextColor getTextColor | ?attr/colorOnSurface (see all states) |
Typography | android:textAppearance | setTextAppearance | ?attr/textAppearanceLabelLarge |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Color | app:backgroundTint | setBackgroundColor setBackgroundTintList getBackgroundTintList | @android:color/transparent (see all states) |
Stroke color | app:strokeColor | setStrokeColor setStrokeColorResource getStrokeColor | ?attr/colorOnSurface at 12% opacity (see all states) |
Stroke width | app:strokeWidth | setStrokeWidth setStrokeWidthResource getStrokeWidth | 1dp |
Shape | app:shapeAppearance | setShapeAppearanceModel getShapeAppearanceModel | ?attr/shapeAppearanceSmallComponent |
Elevation | app:elevation | setElevation getElevation | 0dp |
Ripple color | app:rippleColor | setRippleColor setRippleColorResource getRippleColor | ?attr/colorOnSurface at 16% opacity (see all states) |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Icon | app:icon | setIcon setIconResource getIcon | null |
Color | app:iconTint | setIconTint setIconTintResource getIconTint | ?attr/colorOnSurface (see all states) |
Size | app:iconSize | setIconSize getIconSize | wrap_content |
Gravity (position relative to text label) | app:iconGravity | setIconGravity getIconGravity | start |
Padding (space between icon and text label) | app:iconPadding | setIconPadding getIconPadding | 8dp |
Element | Style |
---|---|
Default style | Widget.Material3.Button.OutlinedButton |
Icon style | Widget.Material3.Button.OutlinedButton.Icon |
Default style theme attribute: ?attr/materialButtonOutlinedStyle
See the full list of styles and attrs.
Text buttons have less visual prominence, so should be used for low emphasis actions, such as when presenting multiple options.
Text button examples
API and source code:
The following example shows a text button with a text label.
Adding an icon to a text button
The following example shows a text button with an icon.
Anatomy and key properties
A text button has a text label, a transparent container and an optional icon.
Text label attributes
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Text label | android:text | setText getText | null |
Color | android:textColor | setTextColor getTextColor | ?attr/colorOnSurface (see all states) |
Typography | android:textAppearance | setTextAppearance | ?attr/textAppearanceLabelLarge |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Color | app:backgroundTint | setBackgroundColor setBackgroundTintList getBackgroundTintList | @android:color/transparent (see all states) |
Stroke color | app:strokeColor | setStrokeColor setStrokeColorResource getStrokeColor | null |
Stroke width | app:strokeWidth | setStrokeWidth setStrokeWidthResource getStrokeWidth | 0dp |
Shape | app:shapeAppearance | setShapeAppearanceModel getShapeAppearanceModel | ?attr/shapeAppearanceSmallComponent |
Elevation | app:elevation | setElevation getElevation | 0dp |
Ripple color | app:rippleColor | setRippleColor setRippleColorResource getRippleColor | ?attr/colorOnSurface at 16% opacity (see all states) |
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Icon | app:icon | setIcon setIconResource getIcon | null |
Color | app:iconTint | setIconTint setIconTintResource getIconTint | ?attr/colorOnSurface (see all states) |
Size | app:iconSize | setIconSize getIconSize | wrap_content |
Gravity (position relative to text label) | app:iconGravity | setIconGravity getIconGravity | start |
Padding (space between icon and text label) | app:iconPadding | setIconPadding getIconPadding | 8dp |
Element | Style |
---|---|
Default style | Widget.Material3.Button.TextButton |
Icon style | Widget.Material3.Button.TextButton.Icon |
Full Width Buttons | Widget.Material3.Button.TextButton.Dialog.FullWidth |
Default style theme attribute: ?attr/borderlessButtonStyle
See the full list of styles and attrs.
Toggle buttons can be used to select from a group of choices.
There are two types of toggle buttons:
To emphasize groups of related toggle buttons, a group should share a common container.
Toggle button examples
API and source code:
The following example shows a toggle button with three buttons that have text labels.
Implementing an icon-only toggle button
The following example shows a toggle button with three buttons that have icons.
Anatomy and key properties
A toggle button has a shared stroked container, icons and/or text labels.
Element | Attribute | Related method(s) | Default value |
---|---|---|---|
Single selection | app:singleSelection | setSingleSelection isSingleSelection | false |
Selection required | app:selectionRequired | setSelectionRequired isSelectionRequired | false |
Element | Style |
---|---|
Default style | Widget.Material3.MaterialButtonToggleGroup |
Default style theme attribute: ?attr/materialButtonToggleGroupStyle
See the full list of styles and attrs.
Icons can be used as toggle buttons when they allow selection, or deselection, of a single choice, such as marking an item as a favorite.
API and source code:
Note The CheckBox API is just one of several inputs that can implement the icon button. See other selection controls for more details.
The following example shows an icon that can be used independently or in items of a RecyclerView .
Buttons support Material Theming and can be customized in terms of color, typography and shape.
Button theming example
API and source code:
The following example shows text, outlined and filled button types with Material Theming.
Implementing button theming
Use theme attributes and styles in res/values/styles.xml to add the theme to all buttons. This affects other components:
Use default style theme attributes, styles and theme overlays. This adds the theme to all buttons but does not affect other components:
Use one of the styles in the layout. That will affect only this button:
Источник