background

As UI styles have evolved over the years, shadows have become an integral part of the design of many apps. But Android doesn’t have a good way to do this.

Here’s a summary of a comprehensive article on Android shadow effects that’s worth a look. Talk about shading in Material Design!

The above article explains the various implementations of shadows in Android, so I won’t go into details here. Although there are many ways to provide, there are great limitations, which are embodied in the following two aspects:

  • Cannot change the color of the shadow;
  • Compatibility problems exist;

Although the principle of using Fab or CardView to implement shadows can be implemented in the way provided by the author, it is relatively cumbersome, but here we provide a simple implementation.

Implement ideas

To add a shadow to a View, you simply provide a shaded background for the View, so there are two ways to do this:

  • Override the View’s onDraw() method;
  • Customize Drawable;

The first option is obviously unreasonable. We can’t override onDraw() for every View that needs to be shaded, so we’ll do it with a custom Drwable (by setting the Paint’s ShadowLayer). Note that this way of implementing the shadow, its target View needs to have hardware acceleration turned off.

view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Copy the code

Demand point

  • Can set the shadow color, rounded corners, area, offset;
  • You can set the background of the View shape, color, rounded corners;

implementation

Source address: ShadowDrawable

public class ShadowDrawable extends Drawable { private Paint mPaint; private int mShadowRadius; // Private int mShape; Private int mShapeRadius; // Private int mOffsetX; Private int mOffsetY; // Private int mOffsetY; Private int mBgColor[]; // Private RectF mRect; public final static int SHAPE_ROUND = 1; Public final static int SHAPE_CIRCLE = 2; Private ShadowDrawable(int shape, int[] bgColor, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) { this.mShape = shape; this.mBgColor = bgColor; this.mShapeRadius = shapeRadius; this.mShadowRadius = shadowRadius; this.mOffsetX = offsetX; this.mOffsetY = offsetY; mPaint = new Paint(); mPaint.setColor(Color.TRANSPARENT); mPaint.setAntiAlias(true); mPaint.setShadowLayer(shadowRadius, offsetX, offsetY, shadowColor); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)); } @Override public void setBounds(int left, int top, int right, int bottom) { super.setBounds(left, top, right, bottom); mRect = new RectF(left + mShadowRadius - mOffsetX, top + mShadowRadius - mOffsetY, right - mShadowRadius - mOffsetX, bottom - mShadowRadius - mOffsetY); } @Override public void draw(@NonNull Canvas canvas) { if (mShape == SHAPE_ROUND) { canvas.drawRoundRect(mRect, mShapeRadius, mShapeRadius, mPaint); Paint newPaint = new Paint(); if (mBgColor ! = null) { if (mBgColor.length == 1) { newPaint.setColor(mBgColor[0]); } else { newPaint.setShader(new LinearGradient(mRect.left, mRect.height() / 2, mRect.right, mRect.height() / 2, mBgColor, null, Shader.TileMode.CLAMP)); } } newPaint.setAntiAlias(true); canvas.drawRoundRect(mRect, mShapeRadius, mShapeRadius, newPaint); } else { canvas.drawCircle(mRect.centerX(), mRect.centerY(), Math.min(mRect.width(), mRect.height())/ 2, mPaint); } } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { mPaint.setColorFilter(colorFilter); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; }}Copy the code

Because of the more provided properties, in order to facilitate the use of the Builder chain to create a way, while providing several commonly used static methods, shadowable can be set as long as a line of code, the specific view shadowDrawable.java.

public static void setShadowDrawable(View view, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
	ShadowDrawable drawable = new ShadowDrawable.Builder()
			.setShapeRadius(shapeRadius)
			.setShadowColor(shadowColor)
			.setShadowRadius(shadowRadius)
			.setOffsetX(offsetX)
			.setOffsetY(offsetY)
			.builder();
	view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
	ViewCompat.setBackground(view, drawable);
}
Copy the code

Effect of instance

Pay attention to the point

  • When setting the color of the shadow, it needs to be transparent (i.e., “#XXXXXXXX” form, such as 50% black [“#80000000”]), not solid;
  • In the above implementation, the shadow is always part of the View, so you need to leave some padding for the shadow to show.

conclusion

Since The Android system does not provide a perfect solution, even if the elevation or translationZ attributes provided above 5.0 are used, the large area of shadows still look relatively hard. Therefore, in Android development, the processing of shadows should be treated according to different situations. It is highly recommended to use 9Patch diagram to solve the shadow of large area, and the above scheme can be used for the shadow of small control.