☰
Android圆角与自定义绘制:从clipPath到60fps的实战路径
2026/10/26 9:54:27 网站建设 项目流程

Android圆角与自定义绘制:从clipPath到60fps的实战路径

【免费下载链接】awesome-android-uiA curated list of awesome Android UI/UX libraries项目地址: https://gitcode.com/gh_mirrors/aw/awesome-android-ui

头像在Figma里是正圆,上线变成带白边的椭圆,PM在群里@你:“晚上能修好吗?”这个Android圆角的坑,你大概率会碰到。awesome-android-ui 收了一堆现成的圆角组件,但选之前,先搞懂圆角到底怎么画出来的,不然库的API看不懂,只能怪自己。

原理速览:圆角到底是怎么画出来的

所有圆角的本质都是“裁剪”,区别只在裁剪发生在哪一层:

  1. clipPath:Canvas层裁剪。自己构造 RoundRect 路径,canvas.clipPath(roundPath)之后画的东西只会出现在路径内。完全可控,代价是每帧都要自己维护路径。
  2. BitmapShader:Paint层“上釉”。把位图包成 shader 再画形状,系统按像素采样图片,圆角被预烘进位图,滚动时只是贴图。
  3. ViewOutlineProvider:View层打码。只告诉框架“轮廓是这个形状”,裁剪、阴影它自己处理,你连 onDraw 都不用进,API 21+ 顺带送阴影。

上面这张是仓库里的 Rotatable,旋转缩放全走 Canvas 变换——和圆角同一套“绘制层”思路:你想露出什么,先说清楚裁掉什么。

选型决策表:先定场景再看库

圆角选型不用背,先看这张表:

需求场景推荐方案一句话理由性能代价
静态背景ShapeDrawable(corners 属性)一行XML搞定零
列表里的圆角图BitmapShader 预烘(RoundedImageView)烘进位图,不逐帧计算一次性位图解码
跨版本圆角卡片CardView / Outline框架统一管角和阴影轻微图层开销
点击半径变化动画自定义View + 逐帧Path唯一能逐帧改的办法逐帧CPU建路径
异形裁剪clipPath + Path全可控,星形六边形都行逐帧建路径

场景拆解

场景A:列表圆角图片不掉帧的写法

问题:一百条的 feed,每格一张 96dp 图,逐帧 clipPath,手指一划,40fps。

解法:别逐帧算,预烘。RoundedImageView 把 BitmapShader 那套封装好了,直接抄作业:

<com.makeramen.roundedimageview.RoundedImageView android:layout_width="96dp" android:layout_height="96dp" app:riv_corner_radius="12dp" app:riv_border_width="1dp" app:riv_border_color="@color/white" android:src="@drawable/avatar"/>

列表的写法关键其实在图片装载链路。像 Kiel 这种声明式适配器(下图)一行数据一行图,圆角只是链路最后那 12dp。

为什么这么选:否决了逐帧 clipPath(每帧新路径,CPU 不给力)和逐格 ShapeDrawable(Drawable 不缓存位图,布局一变就算一遍)。烘成位图之后,滚动就是贴图,60fps 稳。

场景B:带阴影圆角卡片的跨版本一致性

问题:同一张卡片,5.0 上圆角干净阴影柔和,4.4 上角被切方、阴影发虚。用户不管你的 minSdk,只管设计裂没裂。

解法:角和阴影一起交给框架,别自己拼:

<android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="12dp" app:cardElevation="4dp" app:cardPreventCornerOverlap="true"> <!-- 卡片内容 --> </android.support.v7.widget.CardView>

API 21+ 它内部走 Outline,阴影由框架画;老版本 CardView 会自己退化成位图阴影。同一份布局,角是同一个角。

为什么这么选:否决了 9-patch 阴影手搓(每种密度画一张,迟早漏一张)和纯靠系统 elevation(5.0 以下没阴影,裸奔)。

场景C:点击时圆角半径变化的交互动画

问题:关注按钮要从 12dp 胶囊形,点击时长成全圆进度环。两个静态 Drawable 来回切是切不出过渡的——半径本身得是个会动的值。

解法:把圆角半径变成可动画值,逐帧重建 Path:

ValueAnimator anim = ValueAnimator.ofFloat(12f, getHeight() / 2f); anim.addUpdateListener(v -> { cornerRadius = (Float) v.getAnimatedValue(); invalidate(); // onDraw 里用新半径重建 RoundRect }); anim.setDuration(180); anim.start();

单按钮、180ms、几帧 CPU 建路径,无伤大雅。

为什么这么选:否决 Lottie——它是预制的矢量动画(下图),顺滑,但半径没法跟业务状态(成功/失败)动态走;也否决 9-patch 切换,瞬间跳变没有过渡感。

性能暗礁

⚠️ 三块礁石,踩得最多。

逐帧建Path→ onDraw 里每帧path.addRoundRect(...)→ 列表滚动每帧16ms,CPU先扛,帧率先掉。

硬件加速+Xfermode→ Manifest 里android:hardwareAccelerated="false"→ 硬件加速一关,文字渲染全走CPU,老机型直接卡。要用 Xfermode 就得认这笔账。

嵌套布局做阴影→ CardView 外面再套一层 FrameLayout 画阴影 → 多一层就多一层过度绘制,列表滚动时 GPU 翻倍。

圆角组件生态速查

分三组,对号入座:

图片圆角

  • CircleImageView — 圆形头像最小实现,开销几个KB
  • RoundedImageView — 四角半径可独立设,列表格子主力
  • SelectableRoundedImageView — 带选中态的版本,适合可勾选列表

容器圆角

  • CardView — 官方圆角+阴影容器,跨版本最稳
  • Maskable Layout — 任意Path裁剪子控件,clipPath 的容器版
  • MaterialShadows — 跨版本一致的阴影,配圆角卡片用

异形裁剪

  • ShapeImageView — 图片裁成任意形状,圆形星形一把梭
  • android-anyshape — 六边形等多边形,几何老师版异形
  • RoundedLetterView — 圆角字母头像,没图时的兜底

完整清单在仓库 README.md,选库前先翻近6个月有没有 commit;三年没动的库,圆角虽然不会坏,依赖迟早坏,别赌。仓库可直接克隆:git clone https://gitcode.com/gh_mirrors/aw/awesome-android-ui

一句话带走

把你项目里第一个 ImageView 换成 RoundedImageView,滑一下列表,感受差距。

【免费下载链接】awesome-android-uiA curated list of awesome Android UI/UX libraries项目地址: https://gitcode.com/gh_mirrors/aw/awesome-android-ui

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询