Thorough understanding of shadows in Android

If we want to create better Android apps, I believe we need to follow the design guidelines of Material Design. In general, Material Design is a three-dimensional environment that includes lighting, materials, and projections. If we want to follow the design principles of Material Design during the development of the app, then understanding light and shadow is particularly important.

I will try to explain the following topics in this article.

  • 3D in Android
  • Depth
  • Z axis, Elevation and Translation Z.
  • light source
  • Button state (pressed and still)
  • Outline
  • Custom ViewOutlineProvider

Before diving into shadows and light, what do I want to tell you about our real environment?

What is 3D?

The real physical environment is a three-dimensional space, which means that all objects have X, Y and Z dimensions. The Z-axis is vertically aligned with the plane of the display, with the positive Z-axis extending towards the viewer. In the world of Material Design, every object has a thickness of 1dp.

image

Depth of Android 

Material Design differs from other design guidelines because it adds the concept of Depth. Depth is of great significance in real vision.

We can think that we have a layer of paper on our table, and if we stick another sheet of paper, our eyes will perceive it as having a depth.

Let's imagine it with Material Design's app screenshots.

image

Let's take a look at the various elements on the screen.

  • Screen (Surface - Depth 0)
  • CardViews
  • App UI layout
  • Floating Action Button

Here, each element is on top of another. CardView is scrollable, so we can say that the first layer is the scrollable content, the second layer is the AppBar layout, and the third layer (the top layer) is the floating action button.

So, how do we define hierarchy? How do we make users feel the depth? The answer is: the Z axis.

What is the Z value in Android?

A View's Z value has two components:

  • Elevation: height, a static value.
  • Translation Z: Z-axis change value, dynamic value for animation.

I always wonder what is the difference between Elevation and Translation Z.

Elevation is static, so you'd better not change it dynamically. If you want to animate the Z axis (such as a pressed state or a static state), you need to use the Translation Z property.

Translation Z is dynamic, when you create a blank project and add a button to it, when you press it you will see the shadow get bigger. In fact, the Elevation does not change, but the Translation Z property changes. This is Android using the default state list animation, changing the Z property.

Z Vaue = Elevation + TranslationZ

If we change two Views with Z values, let them intersect. How does Android handle layers on the screen? Let me show you a diagram I designed.

image

Another question, how do we see the shadow of an object? Do we need a shadow? No, we need a light source.

What is a light source in Android?

The question is not what? but where.

In reality, if we hold a flashlight and shine on an object on the table (from the top of it), the length of the shadow will shorten, and as you lower it, the length of the shadow will increase.

So in Material Design for Android, where is the light source? At the top? Is it still angled? After some research, I found this phenomenon.

image

There are two light sources in Android, the top one is the key light source and the other is the ambient light source, and the shadows we see are actually a combination of these two light sources.

Let's see the effect displayed.

image

In Android, we have many widgets. Buttons, CardViews, dialogs, drawers, etc. all are views. If there is a light source, we have shadows. So how do we decide the Z value in Android? How does Material Design dictate this?

There is a schematic diagram to reflect this situation.

image

stop or press

As I mentioned before, in the Android Framework, some animations are implemented for widgets. If you put a floating action button in your layout, it will have an Elevation of 6dp by default  . But you will notice that when you press the button, the Elevation of the FAB will increase to 12 dp .

Let me tell you what happened in the process.

In fact, FAB has an  Elevation of 6dp . When you press the button, the translationZ value starts to increase. ViewPropertyAnimator animates the view by changing the translationZ value from 0dp to 6dp. If you release the button, the ViewPropertyAnimator plays the animation, changing the translationZ from 6dp to 0dp. You can create custom state list animations for your views and add them to your views.

Let's take a look at a flowchart of this process.

image

Shadow's Secret: Outline

Outline is a class that belongs to android.graphic, see what its documentation says

Defines a simple shape for the bounding area of ​​the graph.

Can be computed for the View or by the Drawable to drive the shape of the shadow cast by the view, or to clip the content of the view.

Every View has a default outline to show its shadow. If we create a custom shape drawable, its outline will be calculated internally based on its shape. So, if we draw a circle, the outline will be round. If we draw rectangle, the outline will be rectangle.

All in all, there is an  Outlin that allows you to see this effect in an invisible way. But what if I want to create a custom view and change its bounds dynamically? Will Android provide Outline for my custom view?

Android of course provides us with a way to customize Outline, that is:  ViewOutlineProvider.

What is ViewOutlineProvider

In my recent open source  ScalingLayout library, I didn't implement shadow effects for custom views. I thought it was very pretty and had no shadows. But remember the basics of Material Design, 3D, Depth, Z-Value.

image

In this animation, we may not be able to determine which places are clickable, and there is no shadow in the zoom layout.

Next we provide a dynamic outline for the custom view.

public class ScalingLayoutOutlineProvider extends ViewOutlineProvider {

    @Override
    public void getOutline(View view, Outline outline) {
        outline.setRoundRect(0, 0, width, height, radius);
    }
}
public class ScalingLayout extends FrameLayout {
    
    //...
    viewOutline = new ScalingLayoutOutlineProvider(w, h, currentRadius);
    setOutlineProvider(viewOutline);
    //..
    
}

In this way, we add height support for custom Views.

image

More about the simplified basics of using ViewOutlineProvider, you can find details in ScalingLayout.



Author: Chengxiang
Moying Link: https://www.jianshu.com/p/087d4496f72c
Source: Jianshu
The copyright belongs to the author. For commercial reprints, please contact the author for authorization, and for non-commercial reprints, please indicate the source.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325137002&siteId=291194637