方式一:MaterialShapeDrawable
kotlin
implementation "com.google.android.material:material:1.9.0"
MaterialShapeDrawable 是 Android Material Design 组件中的一个非常重要的 drawable 类,用于创建具有 Material Design 形状的背景或装饰效果。它支持各种形状、圆角、阴影、填充、描边和渐变等特性,可以用来为 View 添加更精细的外观效果。 引入
kotlin
fun View.setShadow(
context: Context,
elevationWidth: Float = 10.dp2px().toFloat(),
elevationColor: Int = Color.GRAY,
hasStroke: Boolean = false,
strokeColor: Int = Color.GRAY,
strokeWidth: Float = 2.dp2px().toFloat()
) {
val shapeAppearanceModel = ShapeAppearanceModel.builder()
.setAllCorners(CornerFamily.ROUNDED, 10.dp2px().toFloat())
.build()
val materialShapeDrawable = MaterialShapeDrawable(shapeAppearanceModel).apply {
fillColor = ColorStateList.valueOf(Color.WHITE) //可以设置各个状态颜色(默认颜色、点击颜色等)
if (hasStroke) {
//有轮廓
val padding = (strokeWidth / 2).toInt()
setStroke(strokeWidth, ColorStateList.valueOf(strokeColor)) //设置描边宽度及颜色
setPadding(padding, padding, padding, padding) //设置padding
}
shadowCompatibilityMode = MaterialShapeDrawable.SHADOW_COMPAT_MODE_ALWAYS //阴影兼容模式
initializeElevationOverlay(context)
elevation = elevationWidth// 设置阴影高度
setShadowColor(elevationColor) // 阴影颜色(半透明黑)
}
(this.parent as? ViewGroup)?.clipChildren = false
this.background = materialShapeDrawable
}
利用MaterialShapeDrawable 写了一个View的扩展方法,下面来使用它
kotlin
//XML文件
<TextView
android:id="@+id/tv_shape"
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:text="shadow" />
<!--ImageView外部套一层ViewGroup,以便能使用MaterialShapeDrawable-->
<FrameLayout
android:id="@+id/fl_shape"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="50dp">
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:scaleType="centerCrop"
android:src="@drawable/icon_cat_w"
app:shapeAppearance="@style/roundedCornerStyle" />
</FrameLayout>
Activity中:
kotlin
// 利用MaterialShapeDrawable来设置阴影
private val mTvShape: TextView by id(R.id.tv_shape)
private val mFlShape: FrameLayout by id(R.id.fl_shape)
mTvShape.setShadow(this, hasStroke = true, strokeWidth = 1.dp2px().toFloat())
mFlShape.setShadow(this, elevationColor = Color.RED)
执行结果:
可以看到阴影的颜色和大小都可以设置,还是挺方便的。以下是 MaterialShapeDrawable 中一些关键方法:
1、
setFillColor(ColorStateList fillColor)
:设置 MaterialShapeDrawable 的填充颜色。可以设置为一个 ColorStateList,根据不同的状态(例如按下、聚焦等)设置不同的颜色。2、
setStrokeColor(ColorStateList strokeColor)
:设置 MaterialShapeDrawable 的描边颜色。与填充颜色类似,可以为描边设置 ColorStateList。3、
setStroke(float strokeWidth, @ColorInt int strokeColor)
:这个方法同时设置 MaterialShapeDrawable 的描边宽度和颜色。
4、setCornerSize(float cornerSize)
:设置所有角的圆角大小。你可以通过设置一个统一的 cornerSize 来修改所有角的大小。5、
setShapeAppearanceModel(ShapeAppearanceModel shapeAppearanceModel)
:该方法用于设置 MaterialShapeDrawable 的 ShapeAppearanceModel,可以通过该模型来设置形状、圆角、边框等。6、
initializeElevationOverlay(Context context)
:初始化 elevation overlay 功能。如果 elevationOverlay 被启用,该方法将根据视图的 elevation 修改背景颜色(例如,给视图加上一个阴影)。
7、setShadowCompatibilityMode(@CompatibilityShadowMode int mode)
:设置阴影兼容模式。这个方法控制阴影何时以假阴影绘制,而不是使用原生的 elevation 阴影。8、
isElevationOverlayEnabled()
:检查 elevation overlay 是否启用。该方法返回 true 如果 elevation overlay 在当前主题中启用。9、
getOpacity()
:返回 MaterialShapeDrawable 的不透明度,通常会返回 TRANSLUCENT,表示这个 Drawable 可能是半透明的。
MaterialShapeDrawable 提供了许多灵活的配置选项,可以方便地根据需求设置视图的形状、圆角、阴影、颜色等。它是 Material Design 的核心组件之一,可以通过 ShapeAppearanceModel 来定义外观,而 elevation、shadow 等属性可以带来更丰富的视觉效果。
方式二:CardView
CardView 是 一个专门设计用于显示卡片样式布局的控件。它继承自 FrameLayout,具有丰富的功能,如圆角和阴影效果,非常适合构建具有卡片样式的界面。
kotlin
<!--CardView-->
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
app:cardCornerRadius="10dp"
app:cardElevation="15dp">
<ImageView
android:id="@+id/iv_shadow"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/icon_cat_w" />
</androidx.cardview.widget.CardView>
效果图:

CardView 与阴影相关的几个属性:
cardElevation
: 控制卡片的高度(Z 轴方向),值越大,阴影越明显。cardMaxElevation
: 设置卡片的最大阴影范围,用于限制阴影扩散的程度。cardUseCompatPadding
:在低版本兼容模式下,是否为 CardView 添加额外的内边距以显示阴影。
除了上面两种,还可以考虑直接使用View.setElevation()
方法来实现,不过定制能力差一些。