Android中如何根据自定义Shape图案实现图片裁剪?
实现自定义Shape的图片裁剪方案
首先,你这个Layer-List的Shape本质是顶部带超大圆弧的矩形(那个偏移的椭圆在布局内的可视部分,刚好是顶部圆弧+下方完整矩形的组合效果)。要让图片完全贴合这个形状裁剪,我给你整理了三种实用的实现方式,按需选择即可:
方法一:自定义View + Canvas ClipPath 实时裁剪
这种方式适合直接在布局中显示裁剪后的图片,逻辑和你的Shape完全对齐:
创建自定义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),避免硬件加速下的裁剪异常。在布局中使用
直接替换普通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
相关产品推荐
相关产品推荐

