You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中如何为CardView创建三角形缺口及底部指定样式缺口?

嘿,我来帮你搞定在CardView底部添加三角形缺口的问题!这里有几种实用的方法,你可以根据自己的需求来选:

在CardView上创建底部三角形缺口的几种方法

方法一:自定义Drawable(简单灵活,无需修改View)

这种方法不需要自定义CardView子类,只需要写一个自定义Drawable作为CardView的背景,就能实现带缺口的效果,适合不需要原生阴影的场景。

步骤1:实现自定义Drawable类

import android.graphics.*
import android.graphics.drawable.Drawable

class NotchedCardDrawable(
    private val cornerRadius: Float,
    private val notchSize: Float,
    private val cardColor: Int
) : Drawable() {

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = cardColor
        style = Paint.Style.FILL
    }

    private val path = Path()

    override fun onBoundsChange(bounds: Rect) {
        super.onBoundsChange(bounds)
        updatePath(bounds)
    }

    private fun updatePath(bounds: Rect) {
        path.reset()
        // 先绘制带圆角的矩形基底
        path.addRoundRect(
            bounds.left.toFloat(),
            bounds.top.toFloat(),
            bounds.right.toFloat(),
            bounds.bottom.toFloat(),
            cornerRadius,
            cornerRadius,
            Path.Direction.CW
        )
        // 绘制底部中间的三角形路径,用于裁剪出缺口
        val centerX = bounds.centerX().toFloat()
        val notchPath = Path().apply {
            moveTo(centerX - notchSize, bounds.bottom.toFloat())
            lineTo(centerX, bounds.bottom.toFloat() + notchSize)
            lineTo(centerX + notchSize, bounds.bottom.toFloat())
            close()
        }
        // 通过路径差集,把三角形部分从矩形中"挖掉"
        path.op(notchPath, Path.Op.DIFFERENCE)
    }

    override fun draw(canvas: Canvas) {
        canvas.drawPath(path, paint)
    }

    override fun setAlpha(alpha: Int) {
        paint.alpha = alpha
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
        paint.colorFilter = colorFilter
    }

    override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
}

步骤2:给CardView设置这个Drawable

val cardView = findViewById<CardView>(R.id.your_card_view)
val cornerRadius = resources.getDimension(R.dimen.card_corner_radius)
val notchSize = resources.getDimension(R.dimen.notch_size)
val cardColor = ContextCompat.getColor(this, R.color.card_background)

// 替换CardView的默认背景
cardView.setCardBackgroundDrawable(
    NotchedCardDrawable(cornerRadius, notchSize, cardColor)
)

方法二:自定义CardView子类(保留原生阴影)

如果需要保留CardView的原生阴影效果,最好自定义CardView子类,重写绘制逻辑和阴影轮廓,这样缺口和阴影能完美适配。

步骤1:实现自定义CardView

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat

class NotchedCardView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = androidx.cardview.R.attr.cardViewStyle
) : CardView(context, attrs, defStyleAttr) {

    private var notchSize = 40f // 默认缺口大小
    private val path = Path()
    private val rectF = RectF()

    init {
        // 通过自定义属性配置缺口大小
        attrs?.let {
            val typedArray = context.obtainStyledAttributes(it, R.styleable.NotchedCardView)
            notchSize = typedArray.getDimension(R.styleable.NotchedCardView_notchSize, notchSize)
            typedArray.recycle()
        }
        // 关闭CardView默认背景绘制,我们自己实现
        setCardBackgroundColor(Color.TRANSPARENT)
        // 自定义阴影轮廓
        setOutlineProvider(object : OutlineProvider() {
            override fun getOutline(view: View, outline: Outline) {
                val outlinePath = Path()
                outlinePath.addRoundRect(
                    rectF.set(0f,0f,view.width.toFloat(),view.height.toFloat()),
                    radius, radius, Path.Direction.CW
                )
                val notchPath = Path().apply {
                    moveTo(view.width/2f - notchSize, view.height.toFloat())
                    lineTo(view.width/2f, view.height.toFloat() + notchSize)
                    lineTo(view.width/2f + notchSize, view.height.toFloat())
                    close()
                }
                outlinePath.op(notchPath, Path.Op.DIFFERENCE)
                outline.setConvexPath(outlinePath)
            }
        })
    }

    override fun onDraw(canvas: Canvas) {
        rectF.set(0f,0f,width.toFloat(),height.toFloat())
        path.reset()

        // 绘制带缺口的卡片背景
        path.addRoundRect(rectF, radius, radius, Path.Direction.CW)
        val notchPath = Path().apply {
            moveTo(width/2f - notchSize, height.toFloat())
            lineTo(width/2f, height.toFloat() + notchSize)
            lineTo(width/2f + notchSize, height.toFloat())
            close()
        }
        path.op(notchPath, Path.Op.DIFFERENCE)

        canvas.drawPath(path, Paint().apply {
            color = ContextCompat.getColor(context, R.color.card_background)
            isAntiAlias = true
        })
        super.onDraw(canvas)
    }
}

步骤2:添加自定义属性

在res/values/attrs.xml中添加:

<resources>
    <declare-styleable name="NotchedCardView">
        <attr name="notchSize" format="dimension" />
    </declare-styleable>
</resources>

步骤3:在布局中使用

<com.yourpackage.NotchedCardView
    android:id="@+id/notched_card"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    app:cardCornerRadius="16dp"
    app:notchSize="40dp"
    app:cardElevation="8dp"
    android:layout_margin="16dp"/>

方法三:LayerList快速实现(适合简单场景)

如果缺口颜色和父布局背景一致,可以用LayerList叠加一个三角形来模拟缺口,这种方法最快速,但灵活性差。

创建res/drawable/card_with_notch.xml:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 卡片基底 -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/card_background" />
            <corners android:radius="16dp" />
        </shape>
    </item>
    <!-- 底部白色三角形,模拟缺口(需和父布局背景色一致) -->
    <item android:gravity="bottom|center_horizontal">
        <rotate android:fromDegrees="180">
            <shape android:shape="triangle">
                <solid android:color="@color/white" />
                <size android:width="80dp" android:height="40dp" />
            </shape>
        </rotate>
    </item>
</layer-list>

然后设置给CardView:

<androidx.cardview.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="@drawable/card_with_notch"
    app:cardElevation="8dp"
    app:cardCornerRadius="0dp"/>

内容的提问来源于stack exchange,提问作者Mohammad Mirdar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 12:43:12