前言
安卓实现阴影大致有三种方式
- 使用View自带的elevation属性产生阴影效果
- TextView可通过shadowColor,shadowRadius,shadowDx,shadowDy来控制阴影
- 使用Paint的setShadowLayer功能,绘图的同时绘制阴影
- 通过BlurMaskFilter给Paint设置蒙板滤镜,达到模糊效果,从而画出阴影
其中,第三种是一种最具通用性的方案,它代表了阴影效果的底层实现方式
它不仅适用于安卓,在其它语言中,一般也有类似API与之对应,所以掌握这种方式,对我们长远发展很有帮助
使用elevation属性
elevation有提升高度的意思,控件高度提升后,就会在下方产生阴影效果,这是SDK内部已经实现的功能
这里主要讲解下,elevation属性在使用时的注意事项
- elevation值越大,则根据光线效果,产生的阴影面积越大
- 阴影范围肯定比控件范围大,因为View不能铺满父容器,一定要小于父容器,才能看到阴影效果
- outlineSpotShadowColor可以指定阴影颜色
- outlineProvider可以指定阴影范围,none无阴影,bounds沿控件边界生成阴影,paddedBounds除去padding部分,根据内容边界生成阴影,background根据背景轮廓产生阴影
- 当outlineProvider=background时,必须指定背景,否则无法产生阴影
使用ShadowLayer
ShadowLayer是SDK内部为Paint提供的快捷功能,它允许在绘图的同时,自动生成对应轮廓的阴影
为了提高复用性,我们这里不自定义View,而是将阴影功能封装到一个Drawable里面,这样任何View都能使用
package com.android.architecture;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import com.easing.commons.android.value.color.Colors;
public class ShadowDrawable extends Drawable {
public final int[] solidColor;
public final int shadowColor;
public final int radius;
public final int dx;
public final int dy;
public final Paint solidPaint;
public final Paint shadowPaint;
public final Rect bounds = new Rect();
//ShadowLayer大小和位置计算方式
//每个像素向外扩展radius个像素,同时透明度逐渐变淡,产生模糊效果
//最后整个阴影图层再向右移动dx个像素,向下移动dy个像素
public ShadowDrawable(int[] solidColor, int shadowColor, int dx, int dy, int radius) {
this.solidColor = solidColor;
this.shadowColor = shadowColor;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.shadowPaint = new Paint();
this.solidPaint = new Paint();
//设置阴影画笔
shadowPaint.setAntiAlias(true);
shadowPaint.setColor(shadowColor);
shadowPaint.setStyle(Paint.Style.FILL);
shadowPaint.setShadowLayer(radius, dx, dy, shadowColor);
}
@Override
public void draw(Canvas canvas) {
canvas.drawRoundRect(bounds.left, bounds.top, bounds.right, bounds.bottom, radius, radius, shadowPaint);
canvas.drawRoundRect(bounds.left, bounds.top, bounds.right, bounds.bottom, radius, radius, solidPaint);
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
bounds.left = left + radius;
bounds.top = top + radius;
bounds.right = right - radius - dx;
bounds.bottom = bottom - radius - dy;
//设置实心画笔
LinearGradient shader = new LinearGradient(
0, 0, bounds.width(), bounds.height(),
new int[]{solidColor[0], solidColor[1]},
new float[]{0.0F, 1.0F},
Shader.TileMode.CLAMP
);
solidPaint.setColor(Colors.BLACK);
solidPaint.setShader(shader);
solidPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//重绘
invalidateSelf();
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
Button view = new Button(ctx);
view.setText("OK");
view.setTextColor(Color.WHITE);
Drawable drawable = new ShadowDrawable(new int[]{Colors.RED, Colors.ORANGE}, Colors.ORANGE, 10, 10, 20);
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
view.setBackground(drawable);
通过BlurMaskFilter自己绘制阴影
package com.android.architecture;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatImageView;
@SuppressWarnings("all")
public class A extends AppCompatImageView {
Paint paint;
int radius = 10;
int dx = 5;
int dy = 5;
public A(Context context) {
this(context, null);
}
public A(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init(context, attributeSet);
}
protected void init(Context context, AttributeSet attributeSet) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
setImageResource(R.drawable.app_back_m02);
paint = new Paint();
BlurMaskFilter filter = new BlurMaskFilter(radius, BlurMaskFilter.Blur.NORMAL);
paint.setMaskFilter(filter);
}
@Override
protected void onDraw(Canvas canvas) {
BitmapDrawable drawable = (BitmapDrawable) getDrawable();
Bitmap bitmap = drawable.getBitmap();
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Rect dst1 = new Rect(100 + dx, 100 + dy, 400 + dx, 400 + dy);
Rect dst2 = new Rect(100, 100, 400, 400);
canvas.drawBitmap(bitmap, src, dst1, paint);
canvas.drawBitmap(bitmap, src, dst2, null);
}
}
源码下载
Android实现阴影效果.zip