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
相关产品推荐
相关产品推荐

