Mastering Shadows in Android

If we want to create better apps, I believe that we need to follow material design guideline.In general terms, Material design is a three-dimensional environment containing light, material, and cast shadows. Light and Shadow are important for us if we want to follow material design guideline in our application development process.

I will try to explain following topics in this article.

  • 3D in Android
  • Depth
  • Z value, elevation and Translation Z
  • Light
  • Button state (Pressed and Resting)
  • Outline
  • Custom Outline with ViewOutlineProvider

Before deep dive into shadow and light, I want to show you what our environment is.

The material environment is a 3D space, which means all objects have x, y, and z dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the positive z-axis extending towards the viewer. In Material Design world, every object has 1 dp thickness.

Material design axis system

Material design differs from other design guides because It has depth. We can say that depth defines view’s importance level in user interface. We can think there is a paper layer in our desk. If we put another paper on it, our eyes will perceive that it has a depth.

Let’s imagine it with an app screenshot from the material design guideline.

Sample application diagram shows component elevation along its Z-axis

Let’s see our elements in the screen.

  • Screen (Surface layer — 0 depth)
  • Cardviews
  • Appbar Layout
  • Floating Action Button

Every element has a priority to another. Cardviews can scroll in its recyclerview. So we can say that our first layer is scrollable content. The second layer is appbar layout. The third layer (Top layer) is the floating action button.

So how do we define the order? How do we make the user feel the depth? Answer: Z- value.

The Z value for a view has two components:

  • Elevation: The static component.
  • Translation Z: The dynamic component used for animations.

I always wonder that what is the difference between elevation and translation.

Elevation is static. So you don’t change it dynamically. If you want to animate your view in Z-axis (like pressing and resting) you need to use translation-Z attribute.

Translation Z is dynamic. In your empty project, If you create a button and press it, you will see that shadow gets bigger with an animation. Actually, elevation value is not changing. Translation Z property is changing. Android is using default state list animator changes translation Z property of the view.

Z-Value = Elevation + TranslationZ

What if we change the value of Z of two views that intersect. Does Android handle the order on the screen? Yes. Let me show you that with a diagram that I designed.

Intersect two view

Another question, how do we see a shadow? What do we need to see a shadow. Answer: we need a Light(s).

Actually, question is not What. The question is Where.

I think this is the most surprising part of this article. If we hold a flashlight to the object in the table (from top of it), shadow length will be shorter. As you lower it, shadow length will be increased.

So where is the light coming from in the Android Framework? From the top? Or center? I would go with the center. But after some research, I found that image.

There are two lights in android framework

There are two light in Android Framework. The one in the top is the key light. Another is the ambient light. Our shadows appear with combination of these two lights. Let’s see the result in the surface.

In Android, we have lots of widgets. Button, Card, Dialog, Drawer etc. All of them are view. If there is a view, we have the shadow. So how do we decide Z-value in Android. Material design guideline comes to rescue. There is a value diagram for shadowing our views.

You can find elevation value on the left side

As I mentioned earlier, in Android Framework, some animators are implemented for widgets. If you put a Floating Action Button in your layout, It will have 6 dp elevation by default. But you will notice that fab elevation will be raised to 12 dp when you press the button. Let me tell you what is happening behind the scene.

Actually FAB has 6 dp elevation. When you press the button, translationZ value starts increasing. ViewPropertyAnimator animates your view with changing translation Z value from 0 dp to 6 dp. And If you release the button, ViewPropertyAnimator comes to play and animate translation Z value from 6 dp to 0 dp. You can create a custom state list animator for your views and add it to your view in your xml. Let’s see that with a basic diagram.

ViewPropertyAnimator animates Translation Z property

Outline is an API class belongs to android.graphic package. Let see what documentation says;

Defines a simple shape, used for bounding graphical regions.

Can be computed for a View, or computed by a Drawable, to drive the shape of shadows cast by a View, or to clip the contents of the View.

Every view has default outline to show its shadow. If we create a custom shape drawable, its outline will be calculated internally according to its shape. So, If we draw circle, outline will be circle. If we draw rectangle, outline will be rectangle.

To sum up, There is an Outline that allows you to see the view in a shady way. But what if I want to create a custom view and change its borders dynamically? Will Android provide its outline for my custom view too?

No. But what should we do then? Android should provide us the way that we can give custom outline to my custom view. Exactly.

Here is the magic class comes to the scene: ViewOutlineProvider

ScalingLayout

In my recent library, ScalingLayout, I didn’t implement shadow effect to my custom view. I thought It was even very pretty with no shadow. But remember the basics in material design. 3D, Depth, another word, Z index.

We may not be able to choose exactly in this animation but there is no shadow in the scaling layout. Let’s provide a custom and dynamic outline for the view.

That’s all. Now my custom view has elevation support.

Scaling layout now supports elevation (with custom ViewOutlineProvider)

Gists are simplified to give basics about ViewOutlineProvider. You can find the commit in here.

Happy codings.

Software Engineer at Storytel