Android shape ring圆环 XML绘制进度条背景

6次阅读

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

Android shape ring圆环 XML绘制进度条背景

shape ring 绘制圆环时 stroke 宽度超过半径就消失

Android 的 shape 中用 ring 类型画圆环,本质是画一个「空心圆」——它靠 android:innerRadiusandroid:thickness 共同决定可见区域。如果 android:thickness 太大,导致内圆半径为负或超出外边界,系统直接不渲染。

  • 常见错误现象:ring 在布局里完全不显示,或者只显示一小段弧(尤其在 ProgressBar 背景中)
  • 根本原因:Android 计算外半径 = innerRadius + thickness,但视图宽高固定后,若该值 > 宽高/2,圆环就被裁掉甚至丢弃
  • 实操建议:优先用 android:useLevel="false"(否则 ring 会按 level 缩放,干扰尺寸);设 android:innerRadiusRatioandroid: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 设为 ProgressBarandroid:background,结果进度条(旋转的蓝色弧)和背景圆环错位,看起来像偏心或压扁。

  • 原因:默认 ProgressBar 的进度层(progressDrawable)和背景层(background)使用不同基准对齐 —— 背景按 View 左上角铺,进度层按中心旋转
  • 关键解法:别用 background,改用 android:indeterminateDrawableandroid: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 的正常缩放逻辑在起作用,但 shapestroke 实际按像素绘制,不自动适配 density。

  • 问题根源:shape 是编译期生成的 drawable,dp 值在打包时就转成 px 写死,不会随运行时 density 动态调整
  • 解决方向:要么全用 px(不推荐),要么用 insetscale 层包装,更稳妥的是放弃纯 XML ring,改用自定义 Drawable 或 Jetpack Compose
  • 临时 workaround:在 values-sw360dpvalues-sw480dp 等目录下提供不同尺寸的 ring XML,用 android:thickness="12px" 这类绝对单位,配合 density 换算(比如 xhdpi 下 1dp = 2px,就设 24px)
  • 性能提示:大量使用带 insetscale 的 layer-list 会增加绘制层级,低端机上可能掉帧

复杂点在于 ring 的尺寸逻辑是静态的,而 ProgressBar 的动画、缩放、density 适配全是动态的。XML 里看着对,跑起来偏一点,往往不是写错了,而是没意识到这两套系统根本不在一个坐标系里。

text=ZqhQzanResources