Android的android:angle属性仅支持0、45、90、135、180、225、270、315这8个45°整数倍角度,aapt2在编译期硬校验,非此范围则报错;如需任意角度,须改用LinearGradient+自定义View实现。

Android shape gradient angle 只能填 0、45、90、135、180、225、270、315
Android 的 android:angle 属性在 gradient 中不是任意角度,它只接受 45° 的整数倍(即 0–315 之间,步长 45),否则编译直接报错:Invalid angle for gradient: must be a multiple of 45。这不是运行时限制,是 aapt2 在资源编译阶段就校验的硬规则。
常见错误现象:
- 写成
android:angle="30"或android:angle="120"—— 资源编译失败,AS 报红,APK 打不包 - 用变量或拼接方式动态传角度(比如
@integer/grad_angle)—— 没用,aapt2 不解析引用值,只看 XML 字面量
为什么设计成 45° 倍数?和硬件加速有关
这个限制源于早期 Android 渲染管线对线性渐变的优化策略:系统会把角度映射到 8 个预置方向(水平、垂直、4 个对角),用位图缓存或固定着色器路径加速绘制。即使现在硬件加速普遍,该约束仍保留以保证兼容性和渲染一致性。
影响点:
- 不能实现 30°、60° 这类斜向渐变 —— 没有绕过办法,
shapeXML 层面彻底不支持 - 高版本(如 Android 12+)也没放开,官方文档和源码里仍是硬编码校验
- 如果你真需要非 45° 角度,只能放弃
shape,改用Canvas绘制或Shader自定义 View
替代方案:用 LinearGradient + 自定义 View 实现任意角度
XML 不行,代码可以。核心是用 LinearGradient 构造函数指定起点/终点坐标,从而控制方向。角度换算成坐标时注意:Y 轴向下为正,所以 30° 对应的是 (0, 0) 到 (cos(30°), sin(30°)),但要归一化并适配 View 尺寸。
实操建议:
- 别手算三角函数,用
Math.cos(Math.toRadians(angle))更安全 - 起点终点坐标建议基于 View 宽高动态计算,避免在不同分辨率下偏移
- 记得在
onDraw()里重用Paint和Shader,别每次新建,否则 GC 频繁
简短示例(30° 渐变):
val shader = LinearGradient(
0f, 0f,
width * cos(30.0).toFloat(),
width * sin(30.0).toFloat(),
Color.RED,
Color.BLUE,
Shader.TileMode.CLAMP
)
paint.shader = shader别试图用 rotate 或嵌套 layer-list “模拟”斜角
有人试过先画 45° 渐变再用 rotate 变形,或者用多个 shape 拼接 —— 全部无效。因为:
-
rotate是对整个 Drawable 做仿射变换,渐变纹理本身不会跟着“旋转方向”,只会被拉伸/扭曲,结果不可控 -
layer-list只能叠放,不能合成新角度;叠加多个 45° 渐变也得不到平滑的 30° 效果,边界会露馅 - 所有这些 hack 在 vector drawable 或 tint 适配场景下更易崩,调试成本远高于直接上自定义 View
真正卡住的地方往往不是“怎么写”,而是误以为 XML 有隐藏参数或新版 API 放开了限制 —— 其实没有。只要还用 <gradient> 标签,android:angle 就永远只认那 8 个数。










