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

