Android shape corners radius bottom XML底部圆角设置

7次阅读

android shape drawable 不支持 bottomonly 圆角:android:radius 全局生效,单边属性如 android:bottomleftradius 在 api

Android shape corners radius bottom XML底部圆角设置

shape bottomOnly 圆角在 xml 里根本不存在

Android 的 shape drawable 不支持只设底部两个角的圆角——android:radius 是全局生效的,android:topLeftRadius 等单边属性只有在 android:shape="rectangle" 且 API ≥ 23 时才有效,但它们**不能单独控制底边**;低版本(尤其是主流兼容的 API 16–22)下这些属性会被完全忽略。

常见错误现象:android:bottomLeftRadiusandroid:bottomRightRadius 写了没反应,预览和运行都还是直角。

使用场景:你想要一个上直角、下圆角的卡片背景,比如对话气泡或输入框底部装饰。

实操建议:

  • API ≥ 23 可用 corners 标签配合四个独立 radius 属性,但必须同时写全四个(哪怕设为 0dp),例如:
    <corners android:topLeftRadius="0dp" android:topRightRadius="0dp" android:bottomLeftRadius="8dp" android:bottomRightRadius="8dp" />
  • 要兼容低版本?别碰 shape 单独实现——改用 layer-list + 两个 shape(一个全圆角矩形裁掉上半部分,一个直角矩形盖住上半部)
  • 更推荐直接切图或改用 MaterialCardView 配合 app:shapeAppearanceOverlay,它底层用的是 ShapeAppearanceModel,对单边圆角支持稳定

用 layer-list 模拟 bottom-only 圆角(兼容 API 16+)

这是最稳妥的手动方案,原理是“遮罩”:先画一个完整圆角矩形,再用一个直角矩形把它顶部盖住,只露出底部圆角区域。

关键点在于两个层的尺寸和位置必须精确对齐,否则会出现错位或漏白。

实操建议:

  • 外层 shape 设大一点圆角(比如 16dp),确保底部足够圆润
  • 内层直角 shapeandroid:height 要等于你希望“被削掉”的高度(比如想留出 48dp 高的圆角区,就让直角层高 calc(总高 - 48dp)
  • android:top 把直角层往下推,让它刚好盖住上半部分,例如:
    <item android:top="48dp"><shape><solid android:color="@android:color/transparent"/></shape></item>
  • 注意:所有尺寸单位统一用 dp,不要混用 pxsp

MaterialCardView 的 app:shapeAppearanceOverlay 更省心

如果你项目已引入 Material Components,这是最干净的解法。它不依赖 XML shape 的历史包袱,而是通过 ShapeAppearanceModel 动态生成路径,底部圆角原生支持。

性能影响几乎为零,兼容性由库自身兜底(最低支持 API 14),且能和 elevation、ripple 等特性无缝共存。

实操建议:

  • res/values/styles.xml 中定义 overlay style:
    <style name="BottomRounded" parent=""><item name="cornerSizeTopLeft">0dp</item><item name="cornerSizeTopRight">0dp</item><item name="cornerSizeBottomLeft">8dp</item><item name="cornerSizeBottomRight">8dp</item></style>
  • 在布局中给 MaterialCardView 加属性:
    app:shapeAppearanceOverlay="@style/BottomRounded"
  • 别忘了移除 card 的默认 cardCornerRadius,否则会和 overlay 冲突

自定义 Drawable 类是最后手段

当 layer-list 太难调、Material 又不能用(比如老项目强耦合 appcompat),可以写一个继承 Drawable 的类,重写 onDraw(),用 Path.addRoundRect() 手动构造只含底部圆角的路径。

容易踩的坑:canvas 坐标系容易搞反,RectF 的 top 值不是从上往下算的“高度”,而是距 Canvas 顶部的距离;另外硬件加速开启时,某些 Path 操作可能被降级或失效。

实操建议:

  • 务必在 onBoundsChange() 里重新计算 RectF,否则旋转或缩放后圆角错位
  • Paint.setAntiAlias(true),不然圆角边缘发虚
  • 避免在 onDraw() 里 new 对象(如 PathPaint),提前缓存复用

实际项目里,90% 的底部圆角需求用 MaterialCardView + shapeAppearanceOverlay 就能闭环。剩下 10% 如果卡在老框架里,layer-list 方案虽然啰嗦,但一次写对就能稳定跑五年——比反复调试自定义 Drawable 省心太多。真正麻烦的是那些把圆角逻辑硬塞进 View.onDraw 里还带手势动画的场景,那已经不是 XML 能解决的事了。

text=ZqhQzanResources