When your designer asks you to add shadows to a View, all you need to do is read this article and shadows are no longer a problem.

One, foreword

The world of designers, different from ordinary people, sometimes want to flat style, sometimes want to mimic the style. After the development of Material Design, the concept of height is introduced for UI elements, which can make an element more prominent, show its importance, and make people want to click more.

In skeuomorphic design, the height of the UI element is reflected in the effect of having a shadow on the border, which feels like it’s one level away from the bottom. In the Design of Material Design, the shadow effect is also widely used, such as: FloatingActionButton, CardView and other controls, which support shadow effect by default.

If you want to learn more about shadow Design in Material Design, check out the official documentation.

Material. IO/guidelines /…

Next, let’s take a look at the different ways to implement shadows in different versions of Android.

Take a look at the effect of the implementation, although many, but they are not the same implementation method.

/all_5.jpeg

Second, the effect of shadows

In the world of skeuomorphism, shadows operate primarily on z-properties in three-dimensional space. Here is the introduction of the official website.

The height of the view represented by the Z attribute determines the visual appearance of its shadows: a view with a higher Z value will cast larger and softer shadows. A view with a higher z-value blocks a view with a lower z-value; However, the view’s z-value does not affect the view’s size.

Shadows are drawn by the parent of the promoted view and are therefore subject to standard view clipping, which is performed by the parent by default.

Developer.android.com/training/ma…

The static effect is as follows:

/shadows-depth.png

Plus, dynamic effects should give you a better understanding of shadows.

Use standard apis

Material Design first appeared in Android 5.0 and has since been supported by several Support packages for lower versions.

In Api Level 21, two attributes were added:

  • Elevation: attributes used to elevate the height of UI elements.
  • TranslationZ: Z-axis transformation effect.

These two attributes have corresponding XML attributes and setXxx() methods, and the z-axis changes are mainly determined by these two attributes.

Z = elevation + translationZ

So, if your App’s minSdkVersion is 21, using these two attributes directly is the optimal solution.

3.1 elevation attribute

An elevation attribute, which is used to add a height to a View, can be added directly to the View control and displayed on the interface, as a shadow effect.

In layout-XML layouts, it can be set through the Android :elevation property, and in Java code, it can be used through the view.setelevation () method.

We use the elevation property directly, which accepts a height parameter and can be configured as needed.

It should be noted that the shadow of the View must be projected after the View with the background is visually elevated. That is, a shadow effect similar to lighting. In simple terms, we need to set a Background for the View. We can use the Android: Background attribute or view.setbackground () method to set the Background. Background only requires a Drawable. You can also select an image or a solid color.

Here’s what an elevation attribute looks like:

/setElevation.png

Take a closer look at how elevation attributes are implemented.

/setElevationMethod.png

It is still the operation of mRenderNode. If you trace it down, you will find that it is implemented in a native way, so it is not the effect of using 2D gradient to simulate shadows as we understand.

3.2 translationZ properties

The translationZ property is used to add a z-axis transformation effect to a View. With elevation, it’s one plus one equals two. You can also set the height of the View.

In layout-XML layouts, it can be set through the Android :translationZ property, and in Java code, it can be used through the view.settranslationz () method.

In general, you can set the View directly using the Android :translationZ attributes. When you use them with the Android: Elevation attributes, they add to the height of the View, or you can use just one of them.

TranslationZ is the same as translationX and translationY. The translationZ is the same as translationY. To be clear, there are no Api Level restrictions on X and Y operations.

TranslationZ, like an elevation attribute, requires a Background setting to work, which should be understandable.

Here’s how the translationZ property is set:

/setTranslationZ.png

An implementation using the translationZ property looks very similar to an elevation implementation, and is internally dependent on mRenderNode.

3.3 ViewCompat for Api compatibility

As mentioned earlier, when your minSdkVersion does not meet the requirements of the elevation and translationZ apis, set the Api Level below 21 (Android 5.0). When you use these two properties, you will be given a Warning, and if there is a Lint check during packaging, you will also be given a Warning and the packaging will fail.

But you can also find out what the problem is by reading the hint:

Attribute elevation is only used in API level 21 and higher

If you have specified that versions below Api Level 21 will not be shaded, you can eliminate this Warning by using tools:targetApi=”lollipop” in the layout.

In addition to using build.version_codes.lollipop, if you are using Java code to dynamically set an elevation or translationZ attribute for a View, You can also use ViewCompat, the standard View-compatible class that Android provides, though ViewCompat is recommended.

Now that we’re going to use ViewCompat, let’s see how it works.

/ViewCompatImpl.png

In ViewCompat, there are many interface classes that implement ViewCompatBaseImpl, which correspond to different Api levels and can be implemented in static code blocks based on the Api Level of the currently running device. These, however, are implementations of higher versions inheriting lower versions to achieve inheritance compatibility.

The ViewCompatBaseImpl interface defines a number of apis for operations on views that have different Api versions.

In Api Level 21, both properties are already supported and there is no compatibility issue, so it calls the setElevation() and setTranslationZ() methods directly.

/ViewCompatV21.png

So, we only need to care about implementations below Api Level 21. In general, we do compatibility, and one solution is to simulate the effects of older versions with apis that only exist on older versions; Another option is to drop the lower version and do nothing with it at all.

Let’s look at the scheme that ViewCompat is used for Elevation. In fact, Api Level 21 doesn’t do anything for the operation methods of these two attributes, so you can trace all the way down to ViewCompatBaseImpl.

/ViewCompatBaseImpl.png

As you can see from this, ViewCompat does not make any compatibility between the two methods. On the lower version, no operation is done, which results in no shadow effect on the lower version if you use ViewCompat. No means no, and I won’t show it separately here.

That look at the use of ViewCompat in high version of the renderings, in fact, and before also no difference, but put together to see some clearer.

/Api-demo-pic.png

3.4 Standard Api summary

As you can see by now, if you don’t care about Api level, you can use android:elevation and Android :translationZ to do the shadow effect, and the effect is very good. And its shadow doesn’t actually take up the size of the View’s layout. It spreads out beyond the original layout, so it doesn’t affect the size of the View itself.

However, it has drawbacks. You can only adjust the size of the shadow by setting these two properties, but there is no precise control and no way to change the color of the shadow.

The market share of the latest Android version can be found on this website.

Developer.android.com/about/dashb…

At the time of this writing, it is around 20% below 5.0, and it is up to your product and designer to abandon the shadow effect for this segment of users.

/Android-level.png

If you need a lower-version compatible device, read on to find out how to do it.

Iv. Use 9Patch map

4.1 What is the 9Patch map

Android: Elevation and Android :translationZ will not work if you want to use a device that is compatible with lower versions of Android. They will expire on lower versions and have no effect at all, as long as you have a Warning.

This shadow effect, using the.9 figure, is also a good choice.

.9 The figure is 9Patch, referring to the introduction on the official website:

The Draw 9-Patch tool is a WYSIWYG (what you See is What you get) editor included with Android Studio that allows you to create bitmap images that automatically resize to fit the view’s content and screen size. Selected portions of the image can be scaled horizontally or vertically according to indicators drawn within the image.

Developer.android.com/studio/writ…

4.2 Setting the Shadow using 9Patch

Just make a.9 image with shadows and set up the content area and the stretch area to simulate the shadows.

For example, use a.9 image and set the background on ImageView.

/9patch.png

On layout-XML, just set the Android: Background for the ImageView.

/9patch-xml.png

Let’s see what it does:

/9patch-xiaoguo.png

The shadows set in figure.9 are generally guaranteed. However, it will be set as the background of the View, so the shadow takes up the size of the View, so using the shadow simulated by the image, the visual effect of the View itself will be small.

If you put a single image, you might not see the effect, but if you put together an effect that you use ViewCompat, you can see the comparison.

/9patch-duibi.png

Here, the two ImageViews are actually set to 100dp, but visually, the effect achieved with.9 will be smaller.

4.3 Making 9Patch quickly

The drawings of.9 are generally provided to us by designers. There is also an online tool for creating shadows.

Inloop. Making. IO/shadow4andr…

With this tool, you can make various adjustments to the.9 image, such as rounded corners, size of shadows, color of shadows, etc., all very convenient Settings. The.9 file used in the previous example was created using this tool.

/9patch-web.png

Another way is to use this level of Drawable to simulate shadows, equal to layer upon layer. However, using this method is too troublesome, and the effect is difficult to achieve very good, generally not recommended.

4.4 9Patch Simulation shadow summary

Use.9 figure, make shadow, basically do not need to worry about the effect of the problem, it is also very convenient to use. The only problem is that its shaded areas take up the size of the View itself and cause it to shrink visually.

To sum up, its advantages:

  1. Easy to implement, only need to set the background.
  2. The effect of shadow is controllable, the color, rounded corners and shadow size can be adjusted.

Its disadvantages are also obvious:

  1. In order for the View to visually match the rendering, you need to leave space for shadows.

Fifth, use FAB principle to simulate shadow

We know that some of the Material Design controls on Android have built-in shadow effects and are compatible with older versions. For example, FloatingActionButton and CardView.

So, this summary will look at FloatingActionButton implementation shadow principle.

5.1 Shadow principle of FAB

FAB controls that are supported by the support-design package generally have Support for different Api levels. The same is true in FAB, which implements different logic according to different Api levels.

/fab-createImpl.png

As you can see, different implementation classes are used based on conditions 21, 14, and <14, and they all actually implement the same functionality internally.

If you look closely at the source code of these FAB implementations, you can see that the shadow effects are implemented using a Drawable called ShadowDrawableWrapper.

For example, in FloatingActionButtonGingerbread there is such a set of code.

/fab-gingerbread-background.png

We rely entirely on the ShadowDrawableWrapper for shadow effects.

However, ShadowDrawableWrapper is declared visible within the package, so there is no way to use it directly.

/fab-shadowdrawable-class.png

However, since classes in the supportable design package are usually handled for compatibility, we just need to copy it and its interface DrawableWrapper and use it directly. The source of all of them in the android. Support. The design/widget package below, very easy to find.

It works by wrapping a Drawable in addition to the Drawable you need to set in this article, and then drawing a shadow over that wrapped Drawable.

DrawShadow () : drawShadow() : drawShadow() : drawShadow() : drawShadow() : drawShadow() : drawShadow() : drawShadow() : drawShadow() : drawShadow() : drawShadow() : drawShadow()

If you copy the source code, you will find that it actually supports changing the color of the shadow. If you need to do so, just extend its constructor or configure the corresponding color directly in colors.xml, which sets the color as follows.

/shadowWrapper-method2.png

As you can see, it mainly uses three colors for a gradient shadow effect.

5.2 Use FAB principle to simulate shadow effect

As mentioned above, we just need to copy the ShadowDrawableWrapper and DrawableWrapper files into our project and modify their dependencies a little bit.

/fab-project-path.png

If you copy the source code directly, you’ll see that it also relies on three colors, one for setting the shade, as mentioned earlier. In general, we don’t need to set them, just copy them from the source code.

/fab-shadow-colors.png

We can then dynamically set a shadow effect for the View in Java code.

/fab-shadow-javacode.png

These parameters, you can set according to the effect, their meaning, actually look at the method signature, you know, there is no need to repeat here.

/shadowWrapper-method.png

So let’s take a look at how shadows look using FAB’s ShadowDrawableWrapper.

/fab-xiaoguo.png

5.3 FAB shadow simulation summary

As mentioned above, the principle of ShadowDrawableWrapper is to wrap the Drawable and draw a shadow around it, so it actually needs to take up the space of the View, which will still visually make the View smaller.

However, its shadow color is controllable, which means we can dynamically set the shadow color for it, which should be more flexible.

6. Simulate the shadow implemented by CardView

We know that some of the Material Design controls on Android have built-in shadow effects and are compatible with older versions. For example, FloatingActionButton and CardView.

So, this summary is going to look at how CardView implements shadows.

6.1 Shadow principle of CardView

The CardView is in the support-design package, you won’t find it, it’s in the CardView-v7 package, and it’s now available as a separate reference.

In the cardView-v7 package, there’s very little code.

/cardview-lib-path.png

There are only a few of them, and as you can see, there are some classes that are compatible with the Api version, and so on.

/cardview-impl.png

Among them, there is also a RoundRectDrawableWithShadow class, it’s what we’re going to find, CardView Drawable, It is only used in the CardViewJellybeanMr1 and cardViewGingerbread classes. CardViewApi21 is still used to handle shadows using the setElevation() method.

Before using the experience of the FAB, RoundRectDrawableWithShadow directly copy, then you may find it an error operation. This is mainly because one of the static variables sRoundRectHelper is empty and has not been initialized.

If you look closely at the source code, you will find that it does not implement the same principle in CardViewJellybeanMr1 and CardViewGingerbread. They initialize the sRoundRectHelper variable in the initStatic() method.

CardViewJellybeanMr1. InitStatic () method is as follows:

/cardview-jellybean-initstatic.png

CardViewGingerbread. InitStatic () method is as follows:

/cardview-bread-initstatic.png

You can see how they’re implemented, there’s a big difference.

Know these, we only need RoundRectDrawableWithShadow structure method, according to the Api Level on their different initialization, the code also copy can use directly.

The drawShadow() method is the main focus of the drawShadow() method.

6.2 Take the example of CardView shadows

First, copy the ShadowDrawableWrapper completely into our project, and initialize the sRoundRectHelper with different logic in the constructor according to the Api Level.

We also need to copy the default values for the ShadowDrawableWrapper. Of course, we already have the source code, we can also write it to death, I choose to copy them as is.

/cardview-dims.png

We can in the code, then use the RoundRectDrawableWithShadow.

/cardview-javacode.png

Finally, take a look at the shadow effect implemented:

/cardview-xiaoguo.png

6.3 CardView shadow simulation summary

The shadow effect that CardView simulates, on lower versions, will also take up the View’s original size to draw the shadow, so it will also be visually smaller. However, on higher versions, elevation is still used, which can cause inconsistency between different Api levels.

Use the open source library ShadowLayout

Finally, there is an open source library that uses a LayoutView to implement the shadow effect.

Making address:

Github.com/dmytrodanyl…

Its complete library also has only one class plus some attributes, the entire project structure is as follows.

/shadowlayout-project.png

Several properties are provided to configure the effect of shadows.

/shadowlayout-res.png

It is also very easy to use, it directly inherits from FrameLayout, so it needs to be used as a layout.

/shadowlayout-xml.png

Finally, look at the result.

/shadowlayout-xiaoguo.png

It can basically achieve a shadow-like effect, but it should be an algorithm problem, resulting in the shadow edge is too neat, looks unreal, generally not recommended.

Eight, epilogue

With so much information on how to implement shadow effects on Android, here is a complete picture of the effect. If you have read through this article, I think you should know which solution you should choose.

/WechatIMG30.jpeg

Today in the background of chengxiang Ink shadow public account, reply “growth”. I will send you some of my collated learning materials, including: Android decompilation, algorithms, design patterns, Web project source code.

Recommended reading:

  • Glide used this way, more savings!!
  • If you don’t think about this before jumping ship, it’s a waste of time!!
  • Those years will pay you back
  • How do you do screen adaptation when you only have one device
  • Android source code, online reading more convenient!!

Like it or share it