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

Android开发入门:如何制作自定义形状并在形状内部嵌入图片

实现方案说明

我们采用自定义形状路径+图片裁剪的逻辑实现需求,核心是先定义你需要的特殊形状轮廓,再将图片裁剪到该轮廓范围内即可。下面分别给出传统View体系和Jetpack Compose两种可直接运行的实现方案:


方案1:传统View 实现

步骤1:自定义形状Drawable,生成目标形状轮廓

import android.graphics.*
import android.graphics.drawable.Drawable
import kotlin.math.cos
import kotlin.math.sin

class CustomCutShapeDrawable(
    private val topCornerRadius: Float = 40f, // 顶部圆角大小,可自行调整
    private val bottomCutSize: Float = 60f // 底部斜切的尺寸,可自行调整
) : Drawable() {
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val path = Path()

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

    override fun onBoundsChange(bounds: Rect) {
        super.onBoundsChange(bounds)
        updatePath(bounds.width().toFloat(), bounds.height().toFloat())
    }

    private fun updatePath(width: Float, height: Float) {
        path.reset()
        // 起点:左上角圆角结束位置
        path.moveTo(0f, topCornerRadius)
        // 绘制左上角圆角
        path.quadTo(0f, 0f, topCornerRadius, 0f)
        // 绘制顶部边到右上角圆角起点
        path.lineTo(width - topCornerRadius, 0f)
        // 绘制右上角圆角
        path.quadTo(width, 0f, width, topCornerRadius)
        // 绘制右边到右下角切角起点
        path.lineTo(width, height - bottomCutSize)
        // 绘制右下角斜切
        path.lineTo(width - bottomCutSize, height)
        // 绘制底部边到左下角切角起点
        path.lineTo(bottomCutSize, height)
        // 绘制左下角斜切
        path.lineTo(0f, height - bottomCutSize)
        // 闭合路径形成完整形状
        path.close()
    }

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

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

    override fun getOpacity(): Int {
        return PixelFormat.TRANSLUCENT
    }

    // 对外提供形状路径用于裁剪图片
    fun getShapePath(width: Int, height: Int): Path {
        updatePath(width.toFloat(), height.toFloat())
        return path
    }
}

步骤2:自定义ImageView,实现图片填充到形状内部

import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView

class ShapedImageView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
    private val shapeDrawable = CustomCutShapeDrawable()

    override fun onDraw(canvas: Canvas) {
        // 先将画布裁剪到我们定义的目标形状路径
        canvas.clipPath(shapeDrawable.getShapePath(width, height))
        super.onDraw(canvas)
    }
}

步骤3:布局中直接调用

<com.yourpackage.ShapedImageView
    android:layout_width="200dp"
    android:layout_height="250dp"
    android:scaleType="centerCrop"
    android:src="@drawable/alexandria" />
  • 使用时请将com.yourpackage替换为你项目中ShapedImageView所在的实际包名,把你要填充的Alexandria图片放到drawable目录下,替换@drawable/alexandria即可看到效果。

方案2:Jetpack Compose 实现(更简洁,推荐新项目使用)

不需要自定义View,直接在Composable函数中定义形状裁剪图片即可:

import androidx.compose.foundation.Image
import androidx.compose.foundation.shape.GenericShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp

// 定义目标形状
val CustomCutShape: Shape = GenericShape { size, _ ->
    val topCornerRadius = 40f
    val bottomCutSize = 60f
    val width = size.width
    val height = size.height

    moveTo(0f, topCornerRadius)
    // 左上角圆角
    quadraticBezierTo(0f, 0f, topCornerRadius, 0f)
    // 顶部边
    lineTo(width - topCornerRadius, 0f)
    // 右上角圆角
    quadraticBezierTo(width, 0f, width, topCornerRadius)
    // 右边
    lineTo(width, height - bottomCutSize)
    // 右下角切角
    lineTo(width - bottomCutSize, height)
    // 底部边
    lineTo(bottomCutSize, height)
    // 左下角切角
    lineTo(0f, height - bottomCutSize)
    close()
}

// 使用示例
@Composable
fun ShapedImageDemo() {
    Image(
        painter = painterResource(id = R.drawable.alexandria),
        contentDescription = null,
        modifier = Modifier
            .size(200.dp, 250.dp)
            .clip(CustomCutShape),
        contentScale = ContentScale.Crop
    )
}

内容的提问来源于stack exchange,提问作者Kirollos Mallak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 08:45:05