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

