android 中各种阴影 汇总

在 Android 开发中,阴影(Shadow)是提升 UI 层次感和视觉体验的核心元素,不同视图、组件的阴影实现方式差异较大,需结合场景选择。以下从 基础视图阴影特殊组件阴影自定义阴影 三大维度,汇总 Android 中所有主流阴影实现方案,并对比其特性、兼容性和使用场景。

一、基础视图阴影(通用方案)

适用于 ViewTextViewButton 等普通视图,核心通过 Z 轴高度 或 背景 Drawable 实现,是最常用的阴影方案。

1. elevation + translationZ(官方推荐,API 21+)

Android 5.0(API 21)引入 Material Design 的 Z 轴概念,通过 elevation(静态 Z 轴高度)和 translationZ(动态 Z 轴偏移)控制阴影,阴影的大小、模糊度会随 Z 值 自动变化(Z 值越大,阴影越扩散、越淡)。

核心特性
  • 自动生成阴影:无需手动绘制,系统根据视图的 background(需为带圆角的 Drawable,如 ShapeDrawable)生成阴影,避免阴影与视图边缘 “错位”。
  • 动态可控:支持代码实时调整,适合交互场景(如点击、选中状态)。
  • 兼容性:仅 API 21+ 生效,低版本无效果。
使用方式
XML 中配置
<View
    android:layout_width="200dp"
    android:layout_height="200dp"
    <!-- 1. 静态Z轴高度:决定基础阴影大小 -->
    android:elevation="4dp"
    <!-- 2. 动态Z轴偏移:叠加在 elevation 上,适合临时提升层级 -->
    android:translationZ="2dp"
    <!-- 3. 背景需为带圆角的 Drawable(避免阴影与视图边缘错位) -->
    android:background="@drawable/shape_round_white"
    <!-- 4. 可选:设置阴影颜色(默认是深灰色,API 28+ 支持) -->
    android:shadowColor="#80000000" /> <!-- 80 是透明度(00全透,FF不透) -->
代码中动态调整
// 1. 调整静态 elevation
view.setElevation(6f); // 单位:dp(代码中需传入 float 类型)

// 2. 调整动态 translationZ(如点击时临时提升阴影)
view.setOnClickListener(v -> {
    v.setTranslationZ(8f); // 点击时阴影变大,视图层级提升
    // 延迟恢复默认状态
    v.postDelayed(() -> v.setTranslationZ(0f), 300);
});

// 3. API 28+ 调整阴影颜色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    view.setShadowColor(Color.parseColor("#80FF0000")); // 红色阴影
}
配套 Drawable(shape_round_white.xml)
<!-- 带圆角的白色背景,确保阴影与视图边缘匹配 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@android:color/white" />
    <corners android:radius="8dp" /> <!-- 圆角需与视图一致 -->
</shape>

2. android:shadowXXX(低版本兼容方案,API 1+)

对于 API < 21 的设备,无法使用 elevation,需通过 android:shadowColorandroid:shadowRadiusandroid:shadowDxandroid:shadowDy 手动配置阴影,本质是通过 画布绘制阴影 实现。

核心特性
  • 手动控制阴影参数:需明确设置阴影颜色、模糊半径、偏移量,灵活性高但配置复杂。
  • 兼容性:支持所有 Android 版本,但仅对 文字(TextView) 或 设置了背景 Drawable 的 View 生效(普通 View 需配合 setLayerType(LAYER_TYPE_SOFTWARE, null) 启用软件渲染)。
  • 性能问题:软件渲染(LAYER_TYPE_SOFTWARE)会增加绘制开销,避免在列表(RecyclerView)中大量使用。
使用方式
1. 文字阴影(TextView 专属)

直接通过 shadowXXX 属性设置,无需额外配置:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="带阴影的文字"
    android:textSize="20sp"
    android:shadowColor="#80000000"  <!-- 阴影颜色(带透明度) -->
    android:shadowRadius="4dp"       <!-- 模糊半径(值越大越模糊) -->
    android:shadowDx="2dp"           <!-- 水平偏移(正值向右,负值向左) -->
    android:shadowDy="2dp" />        <!-- 垂直偏移(正值向下,负值向上) -->
2. 普通 View 阴影(需软件渲染)
<View
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@android:color/white"
    android:shadowColor="#80000000"
    android:shadowRadius="6dp"
    android:shadowDx="3dp"
    android:shadowDy="3dp"
    <!-- 关键:API < 21 需启用软件渲染才能显示阴影 -->
    android:layerType="software" />

3. 背景 Drawable 内嵌阴影(自定义形状阴影)

通过 layer-list 或自定义 Drawable 手动绘制阴影,适合需要 非对称阴影(如仅底部阴影)或 复杂形状阴影(如圆形、多边形)的场景。

核心特性
  • 高度自定义:可精确控制阴影的位置、大小、颜色,不受系统默认规则限制。
  • 兼容性:全版本支持,无需依赖 elevation
  • 缺点:需手动计算阴影区域,配置较繁琐。
使用方式(以底部阴影为例)

通过 layer-list 叠加 “阴影层” 和 “内容层”,阴影层用半透明颜色模拟:

<!-- drawable/shadow_bottom.xml -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 1. 阴影层:高度=阴影厚度,背景=半透明灰色 -->
    <item
        android:bottom="0dp"
        android:left="-2dp"
        android:right="-2dp"
        android:top="0dp">
        <shape android:shape="rectangle">
            <solid android:color="#40000000" /> <!-- 阴影颜色(透明度40%) -->
            <corners
                android:bottomLeftRadius="8dp"
                android:bottomRightRadius="8dp"
                android:topLeftRadius="0dp"
                android:topRightRadius="0dp" /> <!-- 仅底部圆角 -->
        </shape>
    </item>

    <!-- 2. 内容层:向上偏移阴影厚度,覆盖阴影层顶部 -->
    <item android:top="4dp"> <!-- 偏移量=阴影厚度 -->
        <shape android:shape="rectangle">
            <solid android:color="@android:color/white" /> <!-- 视图背景色 -->
            <corners android:radius="8dp" /> <!-- 与阴影层圆角匹配 -->
        </shape>
    </item>
</layer-list>

在 View 中引用:

<View
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@drawable/shadow_bottom" />

二、特殊组件阴影(专属方案)

部分 Material 组件或系统组件有专属阴影配置,需使用其特有的属性,而非通用的 elevation

1. CardView 阴影(Material 组件)

CardView 是带圆角和阴影的容器组件,其阴影由 cardElevation 控制(而非普通 elevation),且支持配置阴影颜色和圆角。

核心特性
  • 阴影与圆角绑定:避免普通 View 中 “阴影与圆角错位” 的问题(CardView 会自动根据 cardCornerRadius 生成匹配的阴影)。
  • 兼容性优化:低版本(API < 21)通过 CardView 内部兼容方案显示阴影,无需额外处理。
使用方式

需先引入 Material 依赖(build.gradle)

gradle

implementation 'com.google.android.material:material:1.12.0'

XML 配置:

<com.google.android.material.card.MaterialCardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    <!-- 1. 卡片阴影大小(类似 elevation) -->
    app:cardElevation="4dp"
    <!-- 2. 卡片圆角大小 -->
    app:cardCornerRadius="8dp"
    <!-- 3. 阴影颜色(API 28+ 支持) -->
    app:cardShadowColor="#80000000"
    <!-- 4. 卡片背景色 -->
    app:cardBackgroundColor="@android:color/white"
    <!-- 5. 内边距(避免内容与卡片边缘重叠) -->
    app:contentPadding="16dp">

    <!-- 卡片内部内容 -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CardView 内容" />

</com.google.android.material.card.MaterialCardView>

2. PopupWindow 阴影(弹窗阴影)

PopupWindow 的阴影分为 自身阴影 和 背景蒙层阴影,需分别配置。

1. 自身阴影(弹窗边缘阴影)

通过 setElevation() 或 setBackgroundDrawable() 实现:

// 方式1:API 21+ 用 elevation(推荐)
PopupWindow popupWindow = new PopupWindow(contentView, 300dp, 400dp);
popupWindow.setElevation(8f); // 弹窗自身阴影大小

// 方式2:全版本兼容(用背景 Drawable 带阴影)
popupWindow.setBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.shadow_popup));
2. 背景蒙层阴影(弹窗后面的半透明遮罩)

通过 setBackgroundDrawable() 设置全屏半透明背景,模拟蒙层阴影:

// 1. 设置弹窗可点击外部关闭(必须,否则蒙层不生效)
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);

// 2. 设置蒙层背景(半透明黑色)
Drawable maskDrawable = new ColorDrawable(Color.parseColor("#80000000"));
popupWindow.setBackgroundDrawable(maskDrawable);

// 3. 显示弹窗(如在按钮下方)
popupWindow.showAsDropDown(button);

3. Dialog/AlertDialog 阴影

Dialog 的阴影由其 主题样式 控制,默认主题已包含阴影,可通过自定义主题修改。

自定义 Dialog 阴影
  1. 在 styles.xml 中定义主题:
<style name="CustomDialogStyle" parent="Theme.AppCompat.Light.Dialog">
    <!-- 1. 弹窗背景(带圆角和阴影) -->
    <item name="android:windowBackground">@drawable/dialog_background</item>
    <!-- 2. 弹窗阴影大小(API 21+) -->
    <item name="android:windowElevation">8dp</item>
    <!-- 3. 弹窗边框(可选,避免边缘生硬) -->
    <item name="android:windowFrame">@null</item>
    <!-- 4. 取消默认标题栏 -->
    <item name="windowNoTitle">true</item>
</style>
  1. 定义弹窗背景 dialog_background.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@android:color/white" />
    <corners android:radius="12dp" />
</shape>
  1. 在代码中使用主题:
AlertDialog dialog = new AlertDialog.Builder(this, R.style.CustomDialogStyle)
        .setTitle("自定义阴影 Dialog")
        .setMessage("Dialog 阴影由主题控制")
        .setPositiveButton("确定", null)
        .create();
dialog.show();

三、自定义阴影(进阶方案)

当系统方案无法满足需求(如渐变阴影、不规则形状阴影)时,需通过 自定义 View 绘制阴影 或 使用第三方库 实现。

1. 自定义 View 绘制阴影(Canvas 绘制)

通过 Canvas 的 drawShadow() 方法手动绘制阴影,适合复杂场景。

核心代码
public class CustomShadowView extends View {
    private Paint mPaint;
    private RectF mRect;

    public CustomShadowView(Context context) {
        super(context);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.WHITE); // 视图本身颜色
        mRect = new RectF();

        // 启用阴影绘制(API 21+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setLayerType(LAYER_TYPE_SOFTWARE, mPaint); // 软件渲染(避免硬件加速问题)
            mPaint.setShadowLayer(6f, 3f, 3f, Color.parseColor("#80000000"));
            // 参数说明:阴影模糊半径 → 水平偏移 → 垂直偏移 → 阴影颜色
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRect.set(20dp, 20dp, w - 20dp, h - 20dp); // 视图绘制区域
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 绘制带阴影的矩形(阴影由 mPaint.setShadowLayer() 自动生成)
        canvas.drawRoundRect(mRect, 8dp, 8dp, mPaint);
    }
}

2. 第三方库(简化复杂阴影)

部分库封装了复杂阴影逻辑,适合快速实现特殊效果(如渐变阴影、多层阴影):

  • ShadowLayout:支持自定义阴影颜色、方向、模糊度,兼容低版本。
  • Material Components:除了 CardView,还提供 MaterialButtonBottomSheet 等组件,自带阴影效果。

四、阴影方案对比与选型建议

方案类型核心属性 / API兼容性优势劣势适用场景
elevation + translationZandroid:elevationAPI 21+系统自动生成,动态可控,性能好低版本不支持普通视图、交互场景(如点击提升层级)
android:shadowXXXshadowColor/shadowRadiusAPI 1+全版本兼容,文字阴影便捷需软件渲染,性能差低版本视图、文字阴影
背景 Drawable 内嵌layer-list/shapeAPI 1+全版本兼容,支持非对称阴影配置繁琐,无法动态调整固定形状阴影(如底部阴影)
CardView 专属app:cardElevationAPI 7+(兼容)阴影与圆角匹配,低版本兼容仅限 CardView 组件卡片式布局(如列表项、信息卡片)
PopupWindow/Dialog 阴影setElevation / 主题样式API 21+(部分)弹窗蒙层 + 自身阴影一体低版本需自定义 Drawable弹窗、对话框
自定义 View 绘制Canvas.drawShadow()API 21+支持复杂形状、渐变阴影需自定义代码,学习成本高不规则形状、特殊效果阴影

五、常见问题与解决方案

  1. 阴影与视图圆角错位

    • 原因:普通 View 的 elevation 阴影基于 background 的形状生成,若 background 无圆角,阴影会是直角。
    • 解决方案:给 View 设置带圆角的 background(如 shape Drawable),或使用 CardView
  2. 低版本(API < 21)阴影不显示

    • 解决方案:使用 android:shadowXXX + layerType="software",或背景 Drawable 内嵌阴影。
  3. 阴影显示模糊 / 过淡

    • 调整 elevation(值越大,阴影越扩散)或 shadowRadius(值越大,模糊度越高)。
    • 增加阴影颜色的透明度(如 #80000000 比 #40000000 更浓)。
  4. RecyclerView 中阴影性能差

    • 原因:过多重叠阴影导致过度绘制。
    • 解决方案:使用 elevation(硬件加速),避免 layerType="software";减少不必要的阴影重叠。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值