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

Android中如何根据自定义Shape图案实现图片裁剪?

实现自定义Shape的图片裁剪方案

首先,你这个Layer-List的Shape本质是顶部带超大圆弧的矩形(那个偏移的椭圆在布局内的可视部分,刚好是顶部圆弧+下方完整矩形的组合效果)。要让图片完全贴合这个形状裁剪,我给你整理了三种实用的实现方式,按需选择即可:


方法一:自定义View + Canvas ClipPath 实时裁剪

这种方式适合直接在布局中显示裁剪后的图片,逻辑和你的Shape完全对齐:

  1. 创建自定义ImageView子类
    在onDraw里构建和Shape匹配的Path,用它裁剪Canvas后再绘制图片:

    class ShapeClippedImageView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
    ) : AppCompatImageView(context, attrs, defStyleAttr) {
    
        private val clipPath = Path()
        private val density = resources.displayMetrics.density
        // 和你Shape里的偏移值一一对应:top=-80dp,left/right=-100dp
        private val ovalTopOffset = -80 * density
        private val ovalHorizontalOffset = -100 * density
    
        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            super.onSizeChanged(w, h, oldw, oldh)
            clipPath.reset()
            // 构建和Shape中椭圆完全一致的路径
            val ovalLeft = ovalHorizontalOffset
            val ovalTop = ovalTopOffset
            val ovalRight = w - ovalHorizontalOffset
            val ovalBottom = h.toFloat() // 对应Shape中item的bottom=0dp(和父容器底部对齐)
            clipPath.addOval(ovalLeft, ovalTop, ovalRight, ovalBottom, Path.Direction.CW)
        }
    
        override fun onDraw(canvas: Canvas) {
            canvas.save()
            canvas.clipPath(clipPath)
            super.onDraw(canvas)
            canvas.restore()
        }
    }
    

    👉 兼容提示:如果你的minSdk低于26,建议在构造函数中添加setLayerType(LAYER_TYPE_SOFTWARE, null),避免硬件加速下的裁剪异常。

  2. 在布局中使用
    直接替换普通ImageView,设置图片即可:

    <com.yourpackage.ShapeClippedImageView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:src="@drawable/your_target_image"
        android:scaleType="centerCrop"/>
    

方法二:预生成裁剪后的Bitmap

如果需要把裁剪好的图片保存、复用,用PorterDuffXfermode生成静态Bitmap更合适:

fun getClippedBitmap(sourceBitmap: Bitmap, context: Context): Bitmap {
    val density = context.resources.displayMetrics.density
    val ovalTopOffset = -80 * density
    val ovalHorizontalOffset = -100 * density
    val width = sourceBitmap.width
    val height = sourceBitmap.height

    // 创建空白输出Bitmap
    val outputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(outputBitmap)
    val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    // 先绘制Shape对应的椭圆遮罩
    val clipPath = Path()
    val ovalLeft = ovalHorizontalOffset
    val ovalTop = ovalTopOffset
    val ovalRight = width - ovalHorizontalOffset
    val ovalBottom = height.toFloat()
    clipPath.addOval(ovalLeft, ovalTop, ovalRight, ovalBottom, Path.Direction.CW)
    canvas.drawPath(clipPath, paint)

    // 设置混合模式:只保留遮罩和图片重叠的区域
    paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
    // 绘制原图片
    canvas.drawBitmap(sourceBitmap, 0f, 0f, paint)

    return outputBitmap
}

调用这个方法就能得到裁剪完成的Bitmap,后续可以直接设置给ImageView或保存到本地。


方法三:ViewOutlineProvider(API 21+ 高性能方案)

如果你的App只适配Android 5.0以上,用系统自带的Outline实现性能最优,不需要自定义View:

val imageView = findViewById<ImageView>(R.id.your_image_view)
val density = resources.displayMetrics.density
val ovalTopOffset = -80 * density
val ovalHorizontalOffset = -100 * density

imageView.outlineProvider = object : ViewOutlineProvider() {
    override fun getOutline(view: View, outline: Outline) {
        val width = view.width
        val height = view.height
        // 构建和Shape匹配的椭圆Outline
        outline.setOval(
            ovalHorizontalOffset.toInt(),
            ovalTopOffset.toInt(),
            (width - ovalHorizontalOffset).toInt(),
            height
        )
    }
}
imageView.clipToOutline = true

这种方式利用系统硬件加速,流畅度拉满,缺点是仅支持椭圆、圆角矩形这类基础形状,但刚好匹配你的需求。


关键提示

  • 所有代码中的dp转像素逻辑(density)一定要保留,否则不同分辨率设备上裁剪形状会错位。
  • 如果后续修改了Shape的偏移值,记得同步更新代码中的对应参数哦。

内容的提问来源于stack exchange,提问作者Cyrus the Great

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:21:49