android vectordrawable 不支持 clip-path,需用 viewport 调整、shapeableimageview cutoutpath 或 canvas.clippath() 实现裁剪效果。

clip-path 在 Android vector drawable 中根本不起作用
Android 原生 VectorDrawable 不支持 clip-path 属性——哪怕你照着 SVG 写了 android:clipPath 或在 <group></group> 里加 android:clipPath,它也不会生效,也不会报错,只是静默忽略。这是系统级限制,不是写法问题。
常见错误现象:
• XML 里写了 android:clipPath,但预览和运行时图形完全没被裁剪
• 用 Android Studio 的 Vector Asset Studio 导入带 clipPath 的 SVG,生成的 XML 里该属性被自动删掉或留着但无效
• 尝试用 <path></path> 的 android:pathData 模拟裁剪(比如画个圆再 intersect),结果只是多画了一条路径,不是遮罩
- 真正起作用的是
<group></group>的android:clipChildren="false"+ 手动叠加<path></path>做「遮罩模拟」,但这不是矢量裁剪,是视觉欺骗 - 如果目标是「用一个 path 当蒙版盖住另一个 path」,必须改用
LayerDrawable+ 两张独立的VectorDrawable,再靠PorterDuffXfermode合成(仅限代码层) - Android 12+ 的
DynamicColor和 Material You 不改变这一限制,clip-path依然不被解析
用 android:viewportWidth / android:viewportHeight 配合 android:width / android:height 实现“伪裁剪”
这不是真正的遮罩,但能解决 80% 的实际需求:让矢量图只显示视口范围内的部分,超出的直接截掉。关键在于 viewport 和 intrinsic size 的配合。
使用场景:
• 图标需要固定尺寸(比如 24dp × 24dp),但原始 path 数据画得很大或偏移了
• 要复用同一份 vector XML,在不同容器中显示局部区域(如头像框只取圆形中心)
-
android:viewportWidth和android:viewportHeight定义内部坐标系大小(比如24.0),所有pathData坐标都基于它 -
android:width和android:height是最终渲染尺寸(单位 dp),决定缩放比例 - 真正实现“裁剪感”的是把
<group></group>的android:translateX/android:translateY和android:scaleX/android:scaleY配合 viewport 移动/缩放内容,让目标区域刚好填满 viewport
示例:想只显示原图中间 16×16 区域,且居中:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<group
android:translateX="-4.0"
android:translateY="-4.0">
<path
android:fillColor="#FF0000"
android:pathData="M0,0h48v48h-48z"/>
</group>
</vector>
这里 viewport 是 24×24,但 path 画了 48×48;通过平移 -4,把左上角从 (0,0) 拉到 (-4,-4),让中间区域进入 viewport 范围 —— 效果等同于裁剪。
真遮罩必须进代码层:用 Canvas.clipPath() + Picture 或 RenderNode
XML 做不到的事,得靠 Canvas.clipPath()。但注意:不能直接在 onDraw() 里对 VectorDrawable 调用 draw() 前 clip,因为 VectorDrawable.draw() 内部会重置 canvas 状态。
可行路径只有两条:
- 把 vector 转成
Picture,用PictureDrawable绘制,再在自定义 View 的onDraw()里先canvas.clipPath(maskPath),再pictureDrawable.draw(canvas) - Android 8.0+ 可用
RenderNode+RecordedCanvas录制 vector 渲染过程,然后在onDraw()中回放并应用 clip(性能更好,但 API 较新) - 务必注意:clipPath 的坐标系和 vector 的 viewport 坐标系要对齐,通常需按
getIntrinsicWidth()/getIntrinsicHeight()缩放 mask path
容易踩的坑:
• clipPath() 在硬件加速开启时可能失效(尤其带抗锯齿的 path),需调用 setLayerType(LAYER_TYPE_SOFTWARE, null)
• mask path 必须是闭合路径(android:pathData 末尾是 Z),否则 clip 行为未定义
• Picture 方式在 Android 10+ 上有兼容性风险,部分机型会跳过 clip
替代方案:用 ShapeableImageView + cutoutPath 做容器级遮罩
如果你的“裁剪”目标是让图标显示在特定形状(圆、圆角矩形、三角形)内,别硬啃 vector clip,直接换容器。
Material Components 提供的 ShapeableImageView 支持通过 app:shapeAppearanceOverlay 或代码设置 cutoutPath,它会在绘制子 view(包括 vector 图标)前,先对 canvas 做一次 clip。
- 优势:声明式、可复用、适配深色模式和动态颜色
- 限制:只能裁剪整个 ImageView 区域,不能对 vector 内部多个
<path></path>单独遮罩 - 性能影响:比纯 vector 稍重(多了 layer 和 clip 操作),但对图标类小图几乎无感
- 配置要点:
app:srcCompat="@drawable/ic_vector"加app:shapeAppearanceOverlay="@style/Shape.Circle",样式里定义cornerSize或自定义cutoutPath
这方法绕开了 vector XML 的所有限制,是绝大多数 UI 场景下最稳的选择。
真正难的从来不是怎么写 clip-path,而是判断该不该在 vector 层做这件事——90% 的所谓“矢量裁剪需求”,其实属于容器布局或绘制流程的问题。










