Android自定义ImageView:绘制圆形后填充底部30%并在顶部70%放置图片
实现自定义圆形ImageView:底部30%填充+顶部70%显示图片
核心思路
要实现需求,分三步处理:
- 绘制圆形底部30%的填充区域
- 裁剪画布,让ImageView的图片仅显示在圆形顶部70%的区域
- 保留原有逻辑绘制圆形边框
完整实现代码
class CustomCircleImageView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ImageView(context, attrs, defStyleAttr) { private lateinit var radiusPaint: Paint private val fillPaint = Paint(Paint.ANTI_ALIAS_FLAG) private lateinit var fillPath: Path private lateinit var clipPath: Path private var mWidth = 0 private var mHeight = 0 private var mRadius = 0f private var borderWidth = 4f // 可根据需求调整边框宽度 private var classificationText: String = "" // 用于获取填充/边框颜色的分类文本 init { initPaints() } private fun initPaints() { radiusPaint = Paint(Paint.ANTI_ALIAS_FLAG) } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) mWidth = w mHeight = h // 计算圆形半径:减去边框宽度的一半,避免边框超出View范围 mRadius = min(w, h) / 2f - borderWidth / 2f // 构建底部30%填充区域的路径:从完整圆形中减去顶部70%的矩形 fillPath = Path().apply { addCircle(mWidth / 2f, mHeight / 2f, mRadius, Path.Direction.CW) // 顶部70%区域的矩形:从View顶部到圆形中心+0.4*半径的位置 val topRect = RectF(0f, 0f, mWidth.toFloat(), mHeight / 2f + mRadius * 0.4f) op(topRect, Path.Op.DIFFERENCE) } // 构建图片裁剪路径:从完整圆形中减去底部30%的矩形 clipPath = Path().apply { addCircle(mWidth / 2f, mHeight / 2f, mRadius, Path.Direction.CW) // 底部30%区域的矩形:从圆形中心+0.4*半径的位置到View底部 val bottomRect = RectF(0f, mHeight / 2f + mRadius * 0.4f, mWidth.toFloat(), mHeight.toFloat()) op(bottomRect, Path.Op.DIFFERENCE) } } override fun onDraw(canvas: Canvas?) { canvas ?: return // 1. 绘制底部30%填充区域 fillPaint.color = getClassificationColor(classificationText) fillPaint.style = Paint.Style.FILL canvas.drawPath(fillPath, fillPaint) // 2. 裁剪画布,仅保留顶部70%圆形区域,绘制图片 canvas.save() canvas.clipPath(clipPath) super.onDraw(canvas) // 调用ImageView原生绘制逻辑,图片会被限制在裁剪区域内 canvas.restore() // 3. 绘制圆形边框(保留原有逻辑) radiusPaint.color = getClassificationColor(classificationText) radiusPaint.style = Paint.Style.STROKE radiusPaint.strokeWidth = borderWidth canvas.drawCircle(mWidth / 2f, mHeight / 2f, mRadius, radiusPaint) } // 替换为你实际的颜色获取逻辑 private fun getClassificationColor(text: String): Int { return Color.RED } }
关键细节说明
路径计算逻辑:
- 圆形垂直范围为
centerY - mRadius到centerY + mRadius,总高度2*mRadius - 底部30%区域高度为
0.6*mRadius,因此该区域顶部Y坐标为centerY + 0.4*mRadius - 通过
Path.Op.DIFFERENCE运算,从完整圆形中剔除不需要的区域,得到目标路径
- 圆形垂直范围为
画布裁剪:
- 用
canvas.save()和canvas.restore()维护画布状态,避免裁剪影响后续边框绘制 - 裁剪后调用
super.onDraw(canvas),ImageView的图片会自动适配裁剪区域,仅显示顶部70%的圆形部分
- 用
边框适配:
- 计算
mRadius时减去borderWidth/2,确保边框不会超出View可视范围,避免被截断
- 计算
内容的提问来源于stack exchange,提问作者sodiqOladeni
相关产品推荐
相关产品推荐

