No sé si escuchas a menudo estas palabras: Tu sombra no tiene efecto de sombra; tu sombra es demasiado espesa; tu sombra es demasiado espesa; tu sombra es demasiado sólida; el color de tu sombra no es el correcto, tu sombra...
En el desarrollo oficial, hay un enlace llamado aceptación de UI antes del lanzamiento (la aceptación del producto se puede hacer antes y después), principalmente para verificar si el efecto de desarrollo es consistente con el dibujo de diseño. Por supuesto, muchas UI también pueden ser temporalmente modificado...
Volviendo al principio, a menudo surgen preguntas sobre las sombras durante el proceso de aceptación, lo que se puede decir que es tedioso.Recientemente, tengo algo de tiempo para registrar mis implementaciones de sombras conocidas.
Cada efecto en este artículo ha pasado demo
la prueba, siempre debe haber uno que pueda satisfacer las necesidades del diseño.
Resumen de efectos
以下均为真机测试效果
pseudo sombra de forma
La razón por la que se registra este efecto de sombra falsa es porque algunos métodos de implementación de sombra posteriores necesitan usar este conocimiento.
Si no conoce la forma, o si no está familiarizado con ella, puede ir directamente al manual de la niñera de la forma
Efecto
shape_shadow (estilo de forma)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 这里是设置背景色-->
<solid android:color="#ffffff" />
<!-- 设置四周圆角,可统一设置,也可以单独设置某个位置为圆角-->
<corners android:radius="5dp" />
<!-- <corners-->
<!-- android:bottomLeftRadius="5dp"-->
<!-- android:bottomRightRadius="5dp"-->
<!-- android:topLeftRadius="5dp"-->
<!-- android:topRightRadius="5dp" />-->
<!-- 这里设置边框 -->
<stroke
android:width="1dp"
android:color="#eeeeee" />
</shape>
establecer controlesbackground
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:background="@drawable/shape_shadow"
android:gravity="center"
android:text="伪阴影" />
pseudo-sombra de lista de capas
Para ser honesto, nunca antes había usado
layer-list
una forma de ensamblarloshape
, pero también aprendí una ola mientras miraba las sombras.
Al usar layer-list
el método de implementación, puede transponerlo a un control escrito en xml, porque esta también es una capa de ensamblaje
Efecto
capa_sombra
绘制俩个长方形的shape,上层视图添加内边距,就会形成视觉错觉,也是一种伪阴影效果
对比前者,这种方式可以改变底部背景,类似修改伪阴影颜色
对比前者,这种方式可以改变图层边距,类似修改伪阴影深度
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#CAEEEEEE" />
<corners android:radius="2dp" />
</shape>
</item>
<item
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp">
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="2dp" />
</shape>
</item>
</layer-list>
establecer controlesbackground
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:background="@drawable/layer_shadow"
android:gravity="center"
android:text="layer-list 阴影" />
sombra de alzado
elevation
EsMaterial Design
un efecto de sombra provisto, soloAPI21及以上
compatible; no lo he usado mucho antes, cuando escribí la demostración尝试了一下这些属性主要作用于 ViewGroup
Si el enlace de aceptación no es muy estricto, esta implementación también puede pasar la prueba, y el uso también es muy simple. El atributo se usa principalmente. Si no está configurado, el sistema elevation + translationZ + outlineSpotShadowColor
usará outlineSpotShadowColor
el gris predeterminado
- altura de elevación
- profundidad de traducciónZ
- contornoSpotShadowColor color de sombra
Efecto
Cómo utilizar
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="10dp"
android:background="@color/white"
android:elevation="5dp"
android:orientation="vertical"
android:outlineSpotShadowColor="#f00000"
android:translationZ="1dp">
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="elevation 阴影" />
</LinearLayout>
Sombra de CardView
CardView
EsMaterial Design
un control de capa externa (ViewGroup) proporcionado por , soloAPI21及以上
compatible; el método interno parece serelevation + translationZ
un método de combinación
Si no sabe mucho sobre CardView, puede echar un vistazo al efecto de cardado de CardView , que puede lograr rápidamente esquinas redondeadas, sombras, etc.
Efecto
Cómo utilizar
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="10dp"
android:background="@color/white"
android:translationZ="3dp"
app:cardElevation="5dp">
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="CardView 阴影" />
</androidx.cardview.widget.CardView>
.9 Figura Sombras
Los requisitos de diseño provienen del diseño, por lo que también es necesario encontrar una solución a partir del diseño: para usar la imagen .9, el diseño primero debe proporcionar la imagen original que se puede usar para hacer la imagen .9. El efecto se ha realizado en la imagen original, y solo necesitamos ser responsables de hacer la imagen .9.
Muchos diseños no deben proporcionar imágenes .9, por lo que a menudo necesitamos hacer imágenes .9 por nosotros mismos , no las haré aquí, porque las imágenes originales de cada persona son diferentes, por lo que es mejor dominar el método para hacer imágenes .9
项目:像我项目中这样的阴影背景布局(需要设计提供一张一半高度+自带阴影的背景图),.9图可自动拉伸
sombra de control personalizado
Cuando estaba buscando un control de sombra personalizado directamente en Baidu, encontré una biblioteca de tres partes en github y luego tomé una clase de control personalizado ShadowDrawable , que también se puede usar directamente después de la prueba.
Este control admite la configuración dinámica del color de la sombra, controlar las esquinas redondeadas, controlar el color de fondo, etc.
Representaciones oficiales
Clase personalizada ShadowDrawable , se puede copiar directamente
package com.example.kotlindemo;
import android.graphics.Canvas;
import android.graphics.Color;
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.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
public class ShadowDrawable extends Drawable {
private Paint mShadowPaint;
private Paint mBgPaint;
private int mShadowRadius;
private int mShape;
private int mShapeRadius;
private int mOffsetX;
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;
mShadowPaint = new Paint();
mShadowPaint.setColor(Color.TRANSPARENT);
mShadowPaint.setAntiAlias(true);
mShadowPaint.setShadowLayer(shadowRadius, offsetX, offsetY, shadowColor);
mShadowPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
mBgPaint = new Paint();
mBgPaint.setAntiAlias(true);
}
@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 (mBgColor != null) {
if (mBgColor.length == 1) {
mBgPaint.setColor(mBgColor[0]);
} else {
mBgPaint.setShader(new LinearGradient(mRect.left, mRect.height() / 2, mRect.right,
mRect.height() / 2, mBgColor, null, Shader.TileMode.CLAMP));
}
}
if (mShape == SHAPE_ROUND) {
canvas.drawRoundRect(mRect, mShapeRadius, mShapeRadius, mShadowPaint);
canvas.drawRoundRect(mRect, mShapeRadius, mShapeRadius, mBgPaint);
} else {
canvas.drawCircle(mRect.centerX(), mRect.centerY(), Math.min(mRect.width(), mRect.height())/ 2, mShadowPaint);
canvas.drawCircle(mRect.centerX(), mRect.centerY(), Math.min(mRect.width(), mRect.height())/ 2, mBgPaint);
}
}
@Override
public void setAlpha(int alpha) {
mShadowPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
mShadowPaint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public static void setShadowDrawable(View view, Drawable drawable) {
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
ViewCompat.setBackground(view, drawable);
}
/**
* 为指定View添加阴影
* @param view 目标View
* @param shapeRadius View的圆角
* @param shadowColor 阴影的颜色
* @param shadowRadius 阴影的宽度
* @param offsetX 阴影水平方向的偏移量
* @param offsetY 阴影垂直方向的偏移量
*/
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);
}
/**
* 为指定View设置带阴影的背景
* @param view 目标View
* @param bgColor View背景色
* @param shapeRadius View的圆角
* @param shadowColor 阴影的颜色
* @param shadowRadius 阴影的宽度
* @param offsetX 阴影水平方向的偏移量
* @param offsetY 阴影垂直方向的偏移量
*/
public static void setShadowDrawable(View view, int bgColor, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
ShadowDrawable drawable = new ShadowDrawable.Builder()
.setBgColor(bgColor)
.setShapeRadius(shapeRadius)
.setShadowColor(shadowColor)
.setShadowRadius(shadowRadius)
.setOffsetX(offsetX)
.setOffsetY(offsetY)
.builder();
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
ViewCompat.setBackground(view, drawable);
}
/**
* 为指定View设置指定形状并带阴影的背景
* @param view 目标View
* @param shape View的形状 取值可为:GradientDrawable.RECTANGLE, GradientDrawable.OVAL, GradientDrawable.RING
* @param bgColor View背景色
* @param shapeRadius View的圆角
* @param shadowColor 阴影的颜色
* @param shadowRadius 阴影的宽度
* @param offsetX 阴影水平方向的偏移量
* @param offsetY 阴影垂直方向的偏移量
*/
public static void setShadowDrawable(View view, int shape, int bgColor, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
ShadowDrawable drawable = new ShadowDrawable.Builder()
.setShape(shape)
.setBgColor(bgColor)
.setShapeRadius(shapeRadius)
.setShadowColor(shadowColor)
.setShadowRadius(shadowRadius)
.setOffsetX(offsetX)
.setOffsetY(offsetY)
.builder();
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
ViewCompat.setBackground(view, drawable);
}
/**
* 为指定View设置带阴影的渐变背景
* @param view
* @param bgColor
* @param shapeRadius
* @param shadowColor
* @param shadowRadius
* @param offsetX
* @param offsetY
*/
public static void setShadowDrawable(View view, int[] bgColor, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
ShadowDrawable drawable = new ShadowDrawable.Builder()
.setBgColor(bgColor)
.setShapeRadius(shapeRadius)
.setShadowColor(shadowColor)
.setShadowRadius(shadowRadius)
.setOffsetX(offsetX)
.setOffsetY(offsetY)
.builder();
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
ViewCompat.setBackground(view, drawable);
}
public static class Builder {
private int mShape;
private int mShapeRadius;
private int mShadowColor;
private int mShadowRadius;
private int mOffsetX;
private int mOffsetY;
private int[] mBgColor;
public Builder() {
mShape = ShadowDrawable.SHAPE_ROUND;
mShapeRadius = 12;
mShadowColor = Color.parseColor("#4d000000");
mShadowRadius = 18;
mOffsetX = 0;
mOffsetY = 0;
mBgColor = new int[1];
mBgColor[0] = Color.TRANSPARENT;
}
public Builder setShape(int mShape) {
this.mShape = mShape;
return this;
}
public Builder setShapeRadius(int ShapeRadius) {
this.mShapeRadius = ShapeRadius;
return this;
}
public Builder setShadowColor(int shadowColor) {
this.mShadowColor = shadowColor;
return this;
}
public Builder setShadowRadius(int shadowRadius) {
this.mShadowRadius = shadowRadius;
return this;
}
public Builder setOffsetX(int OffsetX) {
this.mOffsetX = OffsetX;
return this;
}
public Builder setOffsetY(int OffsetY) {
this.mOffsetY = OffsetY;
return this;
}
public Builder setBgColor(int BgColor) {
this.mBgColor[0] = BgColor;
return this;
}
public Builder setBgColor(int[] BgColor) {
this.mBgColor = BgColor;
return this;
}
public ShadowDrawable builder() {
return new ShadowDrawable(mShape, mBgColor, mShapeRadius, mShadowColor, mShadowRadius, mOffsetX, mOffsetY);
}
}
}
Cómo utilizar
var testView = findViewById<TextView>(R.id.test_view)
/* 为指定View设置带阴影的背景
* @param view 目标View
* @param bgColor View背景色
* @param shapeRadius View的圆角
* @param shadowColor 阴影的颜色
* @param shadowRadius 阴影的宽度
* @param offsetX 阴影水平方向的偏移量
* @param offsetY 阴影垂直方向的偏移量
*/
ShadowDrawable.setShadowDrawable(
testView, Color.parseColor("#FFFFFF"), 8,
Color.parseColor("#992979FF"), 6, 0, 0
); }
GradientDrawable shadow (para uso propio del proyecto)
Se usa la función de extensión escrita por kt, y es muy conveniente llamar. Actualmente se usa en el proyecto, y la interfaz de usuario también ha pasado la aceptación.
Eché un breve vistazo, y el método utilizado actualmente en el proyecto es el Shape + GradientDrawable
mejor método, porque fue escrito por el jefe de la empresa, y no he mirado cuidadosamente algunos códigos fuente. Las partes clave han sido extraídas y probadas. .normal después
Principalmente dividido en tres partes.
- método de extensión de nivel superior de forma
- ver el método de extensión de nivel superior
- Cómo utilizar
因为具体阴影设置是在代码中统一设置,固相关效果查看总效果图即可
forma método de nivel superior
package com.example.kotlindemo
import android.graphics.drawable.GradientDrawable
typealias ColorInt = Int
typealias Px = Int
typealias FloatPx = Float
internal const val NO_GETTER = "Getter not available"
inline fun shapeDrawable(fill: GradientDrawable.() -> Unit): GradientDrawable =
GradientDrawable().also {
it.gradientType = GradientDrawable.LINEAR_GRADIENT
it.fill()
}
enum class Shape {
RECTANGLE, OVAL, LINE, RING,
}
typealias ShapeInt = Int
fun toInt(s: Shape): ShapeInt = when (s) {
Shape.RECTANGLE -> GradientDrawable.RECTANGLE
Shape.OVAL -> GradientDrawable.OVAL
Shape.LINE -> GradientDrawable.LINE
Shape.RING -> GradientDrawable.RING
}
enum class Orientation {
TOP_BOTTOM, TR_BL, RIGHT_LEFT, BR_TL, BOTTOM_TOP, BL_TR, LEFT_RIGHT, TL_BR,
}
private fun GradientDrawable.toOrientation(orientation: Orientation): GradientDrawable.Orientation =
when (orientation) {
Orientation.TOP_BOTTOM -> GradientDrawable.Orientation.TOP_BOTTOM
Orientation.TR_BL -> GradientDrawable.Orientation.TR_BL
Orientation.RIGHT_LEFT -> GradientDrawable.Orientation.RIGHT_LEFT
Orientation.BR_TL -> GradientDrawable.Orientation.BR_TL
Orientation.BOTTOM_TOP -> GradientDrawable.Orientation.BOTTOM_TOP
Orientation.BL_TR -> GradientDrawable.Orientation.BL_TR
Orientation.LEFT_RIGHT -> GradientDrawable.Orientation.LEFT_RIGHT
Orientation.TL_BR -> GradientDrawable.Orientation.TL_BR
}
var GradientDrawable.shapeEnum: Shape
set(value) {
shape = toInt(value)
}
@Deprecated(message = NO_GETTER, level = DeprecationLevel.HIDDEN) get() = error(NO_GETTER)
fun rectangleGradientShape(
radius: FloatPx = Float.NaN,
colors: IntArray,
orientation: Orientation,
fill: GradientDrawable.() -> Unit = {
}
): GradientDrawable =
shapeDrawable {
shapeEnum = Shape.RECTANGLE
setColors(colors)
this.orientation = toOrientation(orientation)
// DO NOT CHANGE
// RADIUS AND COLOR ORDER IS IMPORTANT FOR RIPPLES!
if (!radius.isNaN()) {
cornerRadius = radius
}
fill.invoke(this)
}
fun rectangleShape(
radius: FloatPx = Float.NaN,
color: ColorInt,
size: Px? = null,
fill: GradientDrawable.() -> Unit = {
}
): GradientDrawable =
shapeDrawable {
shapeEnum = Shape.RECTANGLE
solidColor = color
size?.let {
this.size = it
}
// DO NOT CHANGE
// RADIUS AND COLOR ORDER IS IMPORTANT FOR RIPPLES!
if (!radius.isNaN()) {
cornerRadius = radius
}
fill.invoke(this)
}
fun circleShape(color: ColorInt, size: Px? = null): GradientDrawable = shapeDrawable {
shape = GradientDrawable.OVAL
solidColor = color
size?.let {
this.size = it
}
}
var GradientDrawable.solidColor: ColorInt
set(value) = setColor(value)
@Deprecated(message = NO_GETTER, level = DeprecationLevel.HIDDEN) get() = error(NO_GETTER)
var GradientDrawable.size: Px
set(value) = setSize(value, value)
get() = intrinsicWidth
class Stroke {
var width: Px = -1
var color: ColorInt = -1
var dashWidth: FloatPx = 0F
var dashGap: FloatPx = 0F
}
inline fun GradientDrawable.stroke(fill: Stroke.() -> Unit): Stroke = Stroke().also {
it.fill()
setStroke(it.width, it.color, it.dashWidth, it.dashGap)
}
class Size {
var width: Px = -1
var height: Px = -1
}
inline fun GradientDrawable.size(fill: Size.() -> Unit): Size = Size().also {
fill(it)
setSize(it.width, it.height)
}
class Corners {
var radius: FloatPx = 0F
var topLeft: FloatPx = Float.NaN
var topRight: FloatPx = Float.NaN
var bottomLeft: FloatPx = Float.NaN
var bottomRight: FloatPx = Float.NaN
internal fun FloatPx.orRadius(): FloatPx = takeIf {
it >= 0 } ?: radius
}
fun Corners.render(): FloatArray = floatArrayOf(
topLeft.orRadius(), topLeft.orRadius(),
topRight.orRadius(), topRight.orRadius(),
bottomRight.orRadius(), bottomRight.orRadius(),
bottomLeft.orRadius(), bottomLeft.orRadius()
)
inline fun GradientDrawable.corners(fill: Corners.() -> Unit): Corners = Corners().also {
it.fill()
cornerRadii = it.render()
}
fun GradientDrawable.corners(
radius: FloatPx = 0f,
topLeft: FloatPx = Float.NaN,
topRight: FloatPx = Float.NaN,
bottomLeft: FloatPx = Float.NaN,
bottomRight: FloatPx = Float.NaN
): Corners = Corners().also {
it.radius = radius
it.topLeft = topLeft
it.topRight = topRight
it.bottomLeft = bottomLeft
it.bottomRight = bottomRight
cornerRadii = it.render()
}
Ver funciones de extensión
package com.example.kotlindemo
import android.view.View
import androidx.core.graphics.toColorInt
fun View.warpInWhiteShadow(radius: Float = 0f, topLeft: Float = Float.NaN, topRight: Float = Float.NaN, bottomLeft: Float = Float.NaN, bottomRight: Float = Float.NaN) {
background = rectangleShape(color = "#1AFFFFFF".toColorInt()) {
corners(radius, topLeft, topRight, bottomLeft, bottomRight)
}
translationZ = 6f
}
//application的上下文,我这边demo就不复杂化了,主要是为了尺寸适配更好看一些
//inline val Int.dp: Int
// get() = (this * AppContext.resources.displayMetrics.density + 0.5f).toInt()
//
//inline val Float.dp: Float
// get() = (this * AppContext.resources.displayMetrics.density + 0.5f).toInt().toFloat()
Cómo utilizar
//先设置底层的阴影背景
var selfView = findViewById<TextView>(R.id.self_view)
selfView.warpInWhiteShadow(topLeft = 6f, topRight = 6f)
//再设置控件的背景
selfView.background = rectangleShape(color = Color.WHITE) {
corners(topLeft = 4f, topRight = 4f)
}
resumen xml
Para evitar que algunos amigos vean xml
la configuración, se registra aquí
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:background="@drawable/shape_shadow"
android:gravity="center"
android:text="伪阴影" />
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:background="@drawable/layer_shadow"
android:gravity="center"
android:text="layer-list 阴影" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="10dp"
android:background="@color/white"
android:elevation="5dp"
android:orientation="vertical"
android:translationZ="1dp">
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="elevation 阴影" />
</LinearLayout>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="10dp"
android:background="@color/white"
android:translationZ="3dp"
app:cardElevation="5dp">
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="CardView 阴影" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/test_view"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="自定义 阴影" />
<TextView
android:id="@+id/self_view"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="项目 阴影" />
</LinearLayout>