android中ring型shape绘制圆环时,若stroke宽度超过半径导致外半径超出视图边界,圆环将不显示或被裁剪;progressbar中需用layer-list配合uselevel控制对齐与动画,且dp单位在shape中按密度固定为px,易引发缩放失真。

shape ring 绘制圆环时 stroke 宽度超过半径就消失
Android 的 shape 中用 ring 类型画圆环,本质是画一个「空心圆」——它靠 android:innerRadius 和 android:thickness 共同决定可见区域。如果 android:thickness 太大,导致内圆半径为负或超出外边界,系统直接不渲染。
- 常见错误现象:
ring在布局里完全不显示,或者只显示一小段弧(尤其在ProgressBar背景中) - 根本原因:Android 计算外半径 =
innerRadius + thickness,但视图宽高固定后,若该值 > 宽高/2,圆环就被裁掉甚至丢弃 - 实操建议:优先用
android:useLevel="false"(否则ring会按 level 缩放,干扰尺寸);设android:innerRadiusRatio和android:thicknessRatio更安全,比如innerRadiusRatio="3"表示内半径 = 宽高/2 ÷ 3 - 示例:
<shape android:shape="ring" android:useLevel="false"> <solid android:color="#E0E0E0"/> <size android:width="100dp" android:height="100dp"/> <stroke android:width="10dp" android:color="#666"/> </shape>
注意这里没写innerRadius,靠stroke+size自动撑开成环;但若你硬写innerRadius="50dp",而size只有100dp,外半径就达60dp→ 超出边界,环就没了
ProgressBar 使用 ring shape 作 background 时进度不居中
很多同学把 ring XML 设为 ProgressBar 的 android:background,结果进度条(旋转的蓝色弧)和背景圆环错位,看起来像偏心或压扁。
- 原因:默认
ProgressBar的进度层(progressDrawable)和背景层(background)使用不同基准对齐 —— 背景按 View 左上角铺,进度层按中心旋转 - 关键解法:别用
background,改用android:indeterminateDrawable或android:progressDrawable去叠加两层;或者把 ring 放进layer-list作为 progressDrawable 的底层 - 兼容性注意:API 21+ 可用
android:indeterminateTint单独调色,但老版本必须靠ColorFilter或换drawable - 最小可行结构:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item><shape android:shape="ring" android:useLevel="false"> <solid android:color="@android:color/darker_gray"/> <size android:width="80dp" android:height="80dp"/> </shape></item> <item><rotate android:fromDegrees="0" android:toDegrees="360"> <shape android:shape="ring" android:useLevel="true"> <solid android:color="#2196F3"/> <size android:width="80dp" android:height="80dp"/> </shape> </rotate></item> </layer-list>注意第二层用了android:useLevel="true",这是旋转动画能生效的前提
ring 在不同屏幕密度下线条粗细不一致
你设了 android:strokeWidth="2dp",但在 xhdpi 手机上看比 mdpi 粗一倍 —— 这不是 bug,是 Android 对 dp 的正常缩放逻辑在起作用,但 shape 的 stroke 实际按像素绘制,不自动适配 density。
- 问题根源:
shape是编译期生成的 drawable,dp值在打包时就转成 px 写死,不会随运行时 density 动态调整 - 解决方向:要么全用
px(不推荐),要么用inset或scale层包装,更稳妥的是放弃纯 XML ring,改用自定义Drawable或 Jetpack Compose - 临时 workaround:在
values-sw360dp、values-sw480dp等目录下提供不同尺寸的 ring XML,用android:thickness="12px"这类绝对单位,配合 density 换算(比如 xhdpi 下 1dp = 2px,就设 24px) - 性能提示:大量使用带
inset或scale的 layer-list 会增加绘制层级,低端机上可能掉帧










