VerctorDrawable

Vector Drawable 是 Android 5.0+ 引入的矢量图形格式,相比位图具有无限缩放不失真和体积小的优势。
PathData 命令列表
命令 描述 大小写含义
M 移动到 (MoveTo) M 绝对位置,m 相对位置
L 画直线到 (LineTo) L 绝对位置,l 相对位置
H 水平直线 (Horizontal LineTo) H 绝对位置,h 相对位置
V 垂直直线 (Vertical LineTo) V 绝对位置,v 相对位置
C 三次贝塞尔曲线 (Cubic Bezier Curve) C 绝对位置,c 相对位置
S 平滑三次贝塞尔曲线 (Smooth Cubic Bezier) S 绝对位置,s 相对位置
Q 二次贝塞尔曲线 (Quadratic Bezier Curve) Q 绝对位置,q 相对位置
T 平滑二次贝塞尔曲线 (Smooth Quadratic Bezier) T 绝对位置,t 相对位置
A 椭圆弧线 (ArcTo) A 绝对位置,a 相对位置
Z 关闭路径 (Close Path) Z 或 z(没有相对位置)
贝塞尔曲线的阶数由控制点的数量决定: 一阶贝塞尔曲线:2 个控制点,是一条直线。 二阶贝塞尔曲线:3 个控制点,是一条抛物线。 三阶贝塞尔曲线:4 个控制点,可以生成更复杂的曲线。 N 阶贝塞尔曲线:N+1 个控制点。 贝塞尔曲线第一个点为起点,最后一个点为终点,中间的点为曲线形状控制点。 各个命令的详细说明 命令的负数例如-1 -20 ,指的是反方向 M 100,200 移动到绝对位置 (100,200) m 50,50 继续移动 50 个单位到相对位置 (150,250) L 200,200 画线到绝对位置 (200,200) l 50,0 画线到相对位置 (250,200) H 300 从当前位置水平画线到 X 坐标 300 h -50 向左画 50 个单位 V 400 从当前位置垂直画线到 Y 坐标 400 v 30 向下画 30 个单位 使用控制点绘制三次贝塞尔曲线。 C 50,100 150,100 200,200 (50,100) 和 (150,100) 是控制点 (200,200) 是终点 使用平滑的三次贝塞尔曲线,前一个曲线的终点会自动成为新的控制点。 S 250,150 300,200 使用自动生成的控制点,形成流畅的曲线 语法: s dx2,dy2 dx3,dy3 (x2,y2):第二个控制点(自动生成第一个控制点)。 (x3,y3):终点(End Point) 自动平滑连接:第一个控制点基于前一条 C/S 曲线的第二个控制点对称生成,确保曲线过渡平滑。 减少代码量:只需指定一个控制点和终点,适合连续平滑曲线(如波浪线、复杂路径)。 依赖前一条曲线:必须跟在 C 或 S 指令后,首条曲线需用 C。 一个例子:

    <path
    android:fillColor="@color/purple_200"
    android:strokeWidth="0.1"
    android:strokeColor="@color/white"
    android:pathData="M10,0C5,5,20,8,10,10,S15,18,10,20z"/>

使用控制点绘制二次贝塞尔曲线。 Q 150,50 200,200 (150,50) 是控制点 (200,200) 是终点 使用平滑的二次贝塞尔曲线。 语法:T x y (x, y) 是终点,控制点会自动从上一个 Q/T 的控制点对称推导出来 例如: T 300,300 自动推算控制点,形成平滑的曲线。 如果前面有 Q 或 T 指令,它会用上一个控制点相对于当前点的对称点作为新的控制点。如果前面没有 Q 或 T,则控制点默认就是当前点(等于直线) 自动平滑连接:T 指令会以前一条曲线的控制点为基准,生成一个对称的新控制点,确保连接处平滑过渡。 仅需指定终点,适合连续绘制平滑曲线。 首条曲线必须用 Q:T 不能单独使用,必须跟在 Q 或另一个 T 之后。 一个例子:

    <path
    android:fillColor="@color/purple_200"
    android:strokeWidth="0.1"
    android:strokeColor="@color/white"
    android:pathData="M10,5Q1,10,10,14T10,22z"/>

    <path
    android:fillColor="@color/purple_200"
    android:strokeWidth="0.1"
    android:strokeColor="@color/white"
    android:pathData="M10,5Q1,5,10,10T10,15T10,18z"/>

绘制椭圆弧。 A 50,50 0 1,1 0.1,0 50,50 是椭圆的半径,分别是 x 轴和 y 轴的半径 0 是椭圆的旋转角度 1,1 分别表示大弧标志和顺时针方向,保持1,1即可 1:画大弧(>180°)1:顺时针方向 0.1,0 终点坐标,A就是决定坐标,a是相对坐标,终点相对于起点的偏移(微小偏移使路径闭合) clip-path clip-path 是 Android Vector Drawable 中用于裁剪图形区域的属性,它指定了一个路径,这条路径将用来限制你绘制的图形内容,使其只显示在该路径之内。 clip-path 是局部的,它只对包含它的 <group> 或 <path> 有效 一个例子:

    <vector
        android:width="200dp"
        android:height="200dp"
        android:viewportWidth="200"
        android:viewportHeight="200">

        <!-- 定义裁剪路径 -->
        <clip-path
            android:name="circleClip"
            android:pathData="M100,100 m-50,0 a50,50 0 1,0 100,0 a50,50 0 1,0 -100,0"/>

        <!-- 应用裁剪路径到 path -->
        <group android:clipPath="circleClip">
            <path
                android:fillColor="#FF0000"
                android:pathData="M0,0 h200 v200 h-200 z"/>
        </group>
    </vector>


关键参数
属性 说明
width/height 图像实际大小(dp)
viewportWidth/viewportHeight 虚拟坐标系,pathData 基于此绘制
pathData 矢量路径,语法类似 SVG d 属性
fillColor 填充颜色
strokeColor / strokeWidth 描边颜色和宽度
android:fillType
nonZero	非零环绕规则(默认),路径方向有关(顺时针+1,逆时针-1)
🌀 nonZero(非零环绕规则)
    如果某个点被路径包围多次,方向相加(顺时针为 +1,逆时针为 -1)。
    只要最终结果 ≠ 0,就会被填充。
    路径方向影响结果!
🔍 举个例子:
    一个圈里套一个反方向画的小圈(比如 donut),中间是空的,因为正负方向抵消了。
    如果两个圈方向一样,中间也会被填充。
evenOdd	奇偶规则,路径边界穿过奇数次算内部,偶数次算外部
    不管方向,只数边界穿过的次数。
    奇数次穿过,算里面,填充。偶数次,不填。
    常用于绘制镂空图形、剪纸风格效果。
    典型应用场景
    五角星等星形图案:使用 evenOdd 可以正确填充星形内部而不填充中心区域
    环形/甜甜圈形状:evenOdd 可以只填充环形部分而不填充中心孔洞
    复杂交叉路径:当路径有自交叉时,evenOdd 通常能产生更直观的结果
        
android:tint 给整个矢量图应用一个颜色滤镜。无论你定义了什么 fillColor 或 strokeColor,都会被这个 tint 色覆盖
android:trimPathStart/trimPathEnd/trimPathOffset
 
android:trimPathStart 是 Android Vector Drawable 中 path 元素的一个属性,用于控制路径的可见起点位置,搭配 trimPathEnd 和 trimPathOffset 一起使用,可以实现路径绘制动画效果(比如画线、描边动画等)。属性允许你指定路径绘制的起始位置,即从路径的哪个百分比位置开始绘制。它的值范围是 0 到 1:
0 表示从路径的起点开始绘制(默认值)
1 表示从路径的终点开始绘制(即不绘制任何部分)
0.5 表示从路径的中间点开始绘制
trimPathStart 通常与以下属性一起使用:android:trimPathEnd - 控制路径绘制的结束点,android:trimPathOffset - 控制路径绘制的偏移量
android:fillAlpha/strokeAlpha 设置填充颜色的透明度,控制填充颜色的不透明度(0.0 到 1.0)0.0 表示完全透明,1.0 表示完全不透明如果不设置 fillColor,即使设置了 fillAlpha 也不会显示填充颜色。 可以和 strokeAlpha 搭配使用,分别控制填充和描边的透明度。也可以通过动画控制 fillAlpha 实现“淡入淡出”的效果。如果 fillColor 已经包含 alpha 通道(如 #80FF0000),则实际透明度是 fillAlpha 和 fillColor alpha 的乘积 优先使用 fillAlpha 属性来设置透明度,因为它更直观且易于动画处理
android:strokeMiterLimit 用于控制路径描边时尖角连接(miter join)的最大长度比例。
当两条线段以锐角相交并使用 android:strokeLineJoin="miter"(尖角连接)时,strokeMiterLimit 决定了尖角延伸长度的限制:
作用原理:尖角连接会延伸两条线段的外边缘直到它们相交,形成一个尖角
限制条件:当尖角长度超过 strokeWidth * strokeMiterLimit 时,会自动转换为斜角连接(bevel join)
默认值:4(Android默认值)
尖角长度(miter length)与 strokeWidth 的关系:miterLength = strokeWidth / sin(θ/2) 其中 θ 是两条线段的夹角
当 miterLength/strokeWidth > strokeMiterLimit 时,连接方式会从 miter 降级为 bevel
典型应用场景
    锐角图形:需要保持尖角效果的图形(如星形、箭头)
    避免过长尖刺:防止极锐角产生过长的尖刺
    视觉一致性:确保不同角度的连接保持相似的视觉权重
注意事项
    仅当 strokeLineJoin="miter" 时此属性才有效
    值必须 ≥ 1,通常建议范围在 1-10 之间
    过小的值会导致过多的斜角连接(bevel)
    过大的值可能导致极锐角产生非常长的尖刺
    从 API 级别 21 (Lollipop) 开始支持
一个例子:这样路径就会“从无到有”地被绘制出来,实现线条动画。

    <path
    android:fillColor="@color/purple_200"
    android:strokeWidth="0.1"
    android:strokeColor="@color/white"
    android:trimPathStart="0"
    android:trimPathEnd="0.5"
    android:trimPathOffset="0.1"
    android:pathData="M5,5l5,5" />

    <objectAnimator
    android:duration="1000"
    android:propertyName="trimPathEnd"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType" />

一个例子:这样路径的填充颜色会从透明慢慢变为完全可见。

    <path
    android:fillColor="@color/purple_200"
    android:strokeWidth="0.1"
    android:strokeColor="@color/white"
    android:fillAlpha="0.7"
    android:strokeAlpha="0.5"
    android:pathData="M5,5l5,5h-5z" />

    <objectAnimator
    android:duration="1000"
    android:propertyName="fillAlpha"
    android:valueFrom="0.0"
    android:valueTo="1.0"
    android:valueType="floatType" />

路径变换 在 group上可以设置以下变换属性:
属性 类型 作用
android:rotation float 顺时针旋转角度,单位是度(°)
android:pivotX float 旋转中心的x坐标,如果不设置默认的旋转点是0.0
android:pivotY float 旋转中心的y坐标,如果不设置默认的旋转点是0.0
android:scaleX float X 轴方向缩放系数
android:scaleY float Y 轴方向缩放系数
android:translateX float 沿 X 轴的平移距离(单位:视图坐标单位 viewportWidth中定义的坐标数量)
android:translateY float 沿 Y 轴的平移距离(单位:视图坐标单位 viewportHeight中定义的坐标数量)
推荐使用 group 标签对多个 path统一应用变换 可以用多层group实现多个动画的组合,例如: 外层 group 控制整体缩放 内层 group 控制局部旋转 各自可以有不同动画效果 一个例子:

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="108dp"
    android:viewportHeight="24"
    android:viewportWidth="24"
    android:width="108dp">

    <group
        android:scaleX="0.6"
        android:scaleY="0.6"
        android:translateX="4.8"
        android:translateY="4.8">

        <path android:fillColor="#0528D5"
            android:pathData="M14,0
            Q24,0,24,10,
            L12,12,
            L10,24
            Q0,24,0,14
            L12,12
            Z"/>

        <path android:fillColor="@color/teal_200"
            android:pathData="M10,0
            Q0,0,0,10
            L12,12,
            L14,24
            Q24,24,24,14
            L12,12
            z"/>


         </group>

    </vector>

设置 pivot 让缩放后中心不变,这样缩放时是绕 (50, 50) 缩放的,图形会原地放大,中心不会偏移。

    <group
        android:scaleX="2.0"
        android:scaleY="2.0"
        android:pivotX="50"
        android:pivotY="50">

gradient渐变色设置 在 Android Vector Drawable 中,渐变是在 元素的 fillColor 或 strokeColor 中,通过使用 标签嵌入来实现的。
属性 说明
android:type linear 或 radial
android:startX, startY 渐变起点坐标(相对于 path 的坐标系统,仅线性渐变)
android:endX, endY 渐变终点坐标(仅线性渐变)
android:centerX, centerY 渐变中心(仅径向渐变)
android:gradientRadius 渐变半径,仅对径向渐变有效,相对于 path 的坐标系统
android:tileMode 渐变重复方式:clamp(默认)使用边缘颜色填充外部 / repeat 重复渐变/ mirror 镜像重复渐变
<item>:颜色节点 每个 <gradient> 必须包含一个或多个 <item> 来定义渐变的颜色和位置。

    <item android:offset="0" android:color="#FF0000"/>
    <item android:offset="0.5" android:color="#00FF00"/>
    <item android:offset="1" android:color="#0000FF"/>

一个例子:定义线性渐变色

    <path
        android:pathData="M6,6v12L15,12Z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:startX="6"
                android:startY="12"
                android:endX="15"
                android:endY="12"
                android:type="linear">
                <item android:offset="0" android:color="#FF0000"/>
                <item android:offset="0.5" android:color="#00FF00"/>
                <item android:offset="1" android:color="#0000FF"/>
            </gradient>
        </aapt:attr>

    </path>

一个例子:径向渐变

    <path
    <android:pathData="M6,6v12L15,12Z">

    <aapt:attr name="android:fillColor">
        <gradient
            android:centerX="6"
            android:centerY="12"
            android:gradientRadius="1"
            android:tileMode="mirror"
            android:type="radial">
            <item android:offset="0" android:color="#FF0000"/>
            <item android:offset="1" android:color="#0000FF"/>
        </gradient>
    </aapt:attr>


绘图建议 1、多个 path 分层绘制,使用 group 可以一起变换、旋转多个 path;好处:更易维护、动画支持更强。

    <group>
        <path ... />
        <path ... />
    </group>

2、控制 stroke 描边效果, strokeLineCap: round / butt / square; strokeLineJoin: round / bevel / miter; strokeLineCap是控制线条“端点”的样式 控制线条的端点形状(仅对“开放路径”生效) 当一条线结束时,线的“头尾”应该长什么样?
属性值 说明 效果图(想象一条水平线)
butt 默认,直接截断 ——
round 端点为圆形 ⚫——⚫
square 方形,但超出线末尾一点 ▬▬(两头稍突出)
strokeLineJoin 是控制两条线“拐角处”的连接方式 比如一个折线的两个线段拐角怎么拼接?
属性值 说明 效果
miter 尖角(默认) ↖️🟩↗️
round 圆角连接 拐角是个小圆
bevel 斜角连接 拐角被“削平”

    <path
        android:strokeColor="#000"
        android:strokeWidth="1"
        android:strokeLineCap="round"
        android:strokeLineJoin="round" />


3、结合 AnimatedVectorDrawable 做动画 创建 animated-vector.xml 文件,引用 VectorDrawable 并指定动画; 可做 pathMorph(变形)、颜色变化、旋转、缩放等; 需要 API 21+。 4、动态颜色控制,通过主题属性或 ColorStateList 实现换肤

    <path
        android:fillColor="?attr/colorPrimary"
        android:strokeColor="@color/stateful_color"
        android:strokeWidth="2dp"/>

5、路径动画(API 25+)

    <animated-vector
        xmlns:aapt="http://schemas.android.com/aapt"
        android:drawable="@drawable/ic_base">
    
        <target android:name="star" android:animation="@animator/path_morph"/>
    </animated-vector>

配合 ObjectAnimator 实现路径变形:

    <!-- res/animator/path_morph.xml -->
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <objectAnimator
            android:propertyName="pathData"
            android:duration="300"
            android:valueFrom="M起始路径..."
            android:valueTo="M目标路径..."
            android:valueType="pathType"/>
    </set>

6、渐变填充(API 24+)

    <path>
        <aapt:attr name="android:fillColor">
            <gradient
                android:startColor="#FF00FF"
                android:endColor="#00FFFF"
                android:type="linear"
                android:startX="0"
                android:startY="0"
                android:endX="100%"
                android:endY="100%"/>
        </aapt:attr>
    </path>

7、性能优化技巧 1)简化路径数据 示例优化前: M10 10 L20 10 L20 20 L10 20 Z 优化后: M10 10H20V20H10Z 2)分层渲染复杂图形

    <layer-list>
        <item android:drawable="@drawable/vector_layer1"/>
        <item android:drawable="@drawable/vector_layer2"/>
    </layer-list>

3)启用硬件加速

    imageView.setLayerType(View.LAYER_TYPE_HARDWARE, null);

8、常见问题解决 1)锯齿问题

    <vector android:antialias="true"/>

2)、点击区域不匹配
java

    view.setTouchDelegate(new TouchDelegate(rect, vectorView));

3)、内存泄漏预防
java

    // 在Activity销毁时
    vectorDrawable.setCallback(null);
    

VerctorDrawableAnimator

android verctor结合animator可以做出好看的动画,这里展示一个例子
图像文件存放在drawable/new_icon_test9.xml

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="108dp"
    android:viewportHeight="24"
    android:viewportWidth="24"
    android:width="108dp">

    <path
        android:name="test9"
        android:strokeWidth="0.1"
        android:strokeColor="@color/purple_700"
        android:trimPathStart="0"
        android:trimPathEnd="1"
        android:pathData="M8,5v14l10,-7"/>

        </vector>

动画文件存放在animator/test9_animator.xml,动画文件必须放在这个文件夹下面

    <objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="trimPathEnd"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType"/>

动画和图像连接文件,放在drawable/test9_animator_drawable.xml

    <animated-vector
    android:drawable="@drawable/new_icon_test9"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <target
        android:animation="@animator/test9_animator"
        android:name="test9"/>

        </animated-vector>

实现代码

@OptIn(ExperimentalAnimationGraphicsApi::class)
@Composable
fun test10Animator(){
    var isPlaying by remember { mutableStateOf(false) }
    val animatorDrawable = AnimatedImageVector.animatedVectorResource(R.drawable.test10_animator_drawable)
    val painter = rememberAnimatedVectorPainter(animatorDrawable,isPlaying)
//    Icon(painter,
//        tint = Color.Unspecified, //不加这个属性,会导致图像颜色被默认的黑色覆盖,Icon(...) 组件会默认应用一个颜色 “tint” 到 painter 上,导致你矢量图的原始颜色被覆盖成了黑色。直接用Image就不会
//        contentDescription = "",
//        modifier = Modifier.size(200.dp).clickable{isPlaying = !isPlaying}
//    )
    Image(painter,
        contentDescription = "",
        modifier = Modifier.size(200.dp)
            .clickable{
                isPlaying = !isPlaying
            })
}