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

Android Kotlin:如何绘制带倒圆角的连接矩形并实现弹跳动画

Android自定义View实现带倒圆角的底部矩形及弹跳拉伸动画

一、核心思路

Shape Drawable无法实现向外凸的倒圆角,直接用Canvas结合Path手动绘制是最优方案;动画部分通过ValueAnimator控制绘制参数,同步实现弹跳偏移和顶部拉伸效果。

二、自定义View实现步骤

1. 定义关键属性

先在自定义View中声明所需的样式、动画参数:

class BottomBounceView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    // 顶部圆角半径(左右)
    private val topRoundRadius = dp2px(16f)
    // 底部倒圆角半径(左右,向外凸)
    private val bottomInverseRadius = dp2px(12f)
    // 顶部矩形默认高度
    private var topRectHeight = dp2px(40f)
    // 底部矩形固定高度
    private val bottomRectHeight = dp2px(60f)
    // 弹跳偏移量(控制上下移动)
    private var bounceOffset = 0f
    // 顶部矩形拉伸增量
    private var stretchDelta = 0f

    private val topPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#FF4081")
        style = Paint.Style.FILL
    }
    private val bottomPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#FF212121")
        style = Paint.Style.FILL
    }
    private val path = Path()

    private fun dp2px(dp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics)
    }
}

2. 手动绘制带倒圆角的矩形

通过Path构建顶部矩形轮廓,顶部用正常圆角,底部绘制向外凸的倒圆角:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    val width = width.toFloat()
    // 计算顶部矩形实际高度(默认高度+拉伸增量)
    val actualTopHeight = topRectHeight + stretchDelta
    // 顶部矩形起始Y坐标(底部向上偏移+弹跳偏移)
    val topRectTop = height - actualTopHeight - bottomRectHeight + bounceOffset
    val topRectBottom = topRectTop + actualTopHeight

    // 构建顶部矩形Path
    path.reset()
    // 左上角圆角
    path.moveTo(topRoundRadius, topRectTop)
    path.arcTo(RectF(0f, topRectTop, topRoundRadius*2, topRectTop + topRoundRadius*2), 180f, 90f)
    // 右上角圆角
    path.lineTo(width - topRoundRadius, topRectTop)
    path.arcTo(RectF(width - topRoundRadius*2, topRectTop, width, topRectTop + topRoundRadius*2), 270f, 90f)
    // 右侧边到右下角
    path.lineTo(width, topRectBottom - bottomInverseRadius)
    // 右下角倒圆角(向外凸)
    path.arcTo(RectF(width - bottomInverseRadius*2, topRectBottom, width, topRectBottom + bottomInverseRadius*2), 270f, -90f)
    // 底部边到左下角
    path.lineTo(bottomInverseRadius, topRectBottom + bottomInverseRadius)
    // 左下角倒圆角(向外凸)
    path.arcTo(RectF(0f, topRectBottom, bottomInverseRadius*2, topRectBottom + bottomInverseRadius*2), 180f, -90f)
    // 左侧边回到起点
    path.lineTo(0f, topRectTop + topRoundRadius)
    path.close()

    // 绘制顶部带倒圆角的矩形
    canvas.drawPath(path, topPaint)

    // 绘制底部矩形
    val bottomRectTop = height - bottomRectHeight + bounceOffset
    canvas.drawRect(0f, bottomRectTop, width, height.toFloat(), bottomPaint)
}

3. 实现弹跳+拉伸动画

用ValueAnimator同时控制弹跳偏移和拉伸高度,搭配BounceInterpolator实现弹跳效果:

override fun onAttachedToWindow() {
    super.onAttachedToWindow()
    startBounceAnimation()
}

private fun startBounceAnimation() {
    val animator = ValueAnimator.ofFloat(0f, 1f).apply {
        duration = 1500
        interpolator = BounceInterpolator()
        repeatCount = ValueAnimator.INFINITE
        repeatMode = ValueAnimator.REVERSE
        addUpdateListener { animation ->
            val progress = animation.animatedValue as Float
            // 弹跳偏移范围:上下移动20dp
            bounceOffset = dp2px(20f) * (1 - progress)
            // 顶部拉伸增量:最大拉伸15dp
            stretchDelta = dp2px(15f) * progress
            invalidate()
        }
    }
    animator.start()
}

三、布局中使用

直接在布局文件引用自定义View即可:

<com.yourpackage.BottomBounceView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"/>

四、关键说明

  • 倒圆角实现:通过arcTo绘制反向圆弧,注意起始角度和扫过角度,确保圆弧向外凸
  • 动画联动:通过动画进度同步控制弹跳偏移和拉伸高度,实现连贯效果
  • 性能优化:复用Path对象,初始化时设置Paint抗锯齿,避免重复创建对象

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 17:35:54