首先我们看一下矢量图的文件
显示的是这么个图形
我们来看看每个标签的意思
vector 定义的矢量图,在res/drawable/
android:name 矢量图的名称
android:width drawable的固定高度,支持所有 Android 系统支持的尺寸,通常使用 dp
android:height drawable的固定宽度,支持所有 Android 系统支持的尺寸,通常使用 dp
android:viewportWidth 定义矢量图视图的宽度,视图就是矢量图 path 路径数据所绘制的虚拟画布
android:viewportHeight 定义矢量图视图的高度,视图就是矢量图 path 路径数据所绘制的虚拟画布
android:tint drawable 的 tint 颜色。默认是没有 tint 颜色的
android:tintMode tint 颜色的 Porter-Duff blending 模式,默认值为 src_in
android:autoMirrored 设置当系统为 RTL (right-to-left) 布局的时候,是否自动镜像该图片。比如 阿拉伯语。
android:alpha 该图片的透明度属性
其他的都很好理解,width和viewportWidth是什么意思呢,我们来看一下
viewportWidth和viewportHeight
通过studio生成svg文件
我们修改一下大小
我们对比一下两个文件
可以看到,两个文件基本一样,唯一的不同是width和height,viewportWidth和viewportHeight是一样的,为了方便比较,我们修改一下width、height、viewportWidth、viewportHeight这几个文件来对比一下
svg_ic_add_box_black_120dp_viewportsmall.xml
svg_ic_add_box_black_120dpc_viewportbig.xml
显示在界面上看看效果,为了方便看效果,每个view都加上黑色背景
我们看下实际的效果
分析一下上图:
图1:原始大小,24dp的宽高
图2:ImageView中修改了大小为150dp,矢量图缩放为宽高150dp
图3:矢量图的width和height为120dp,显示大小120dp
图4:矢量图和图3一样,ImageView限制大小,矢量图缩放匹配最小宽高100dp
图5:这里,我们修改了矢量图width="240dp", height="120dp", viewportWidth="12.0", viewportHeight="16.0",我们看到,图片已经显示不全了,原本viewport是24,我们看宽度,改为12,从图像来看,正好是一半,viewport高度16也符合比例,是否可以认为viewport就是控制路径最终显示比例的。宽高显示符合240dp和120dp
图6:矢量图和图5是一样的,修改了ImageView的大小,layout_width="100dp" layout_height="200dp",可以看到矢量缩放为宽度100dp,图形还是没有变的
图7:我们修改了矢量图width="240dp", height="100dp", viewportWidth="12.0", viewportHeight="50.0",和图5对比,同样的就是width和height,再来看看viewport,我们设定viewportWidth="12.0",大小是24的一半,然后viewportHeight="50.0",大小是24的一倍多。看到,图形上,宽显示一半,高超出部分显示透明
得到结论:
viewportWidth和viewportHeight的大小是矢量图路径绘制的基准,比如上面的矢量图,viewport大小是24,那么这个画布就是24*24,如果路径中有点M48,48,那就超出画布边界了,当L0,0时,实际这根线只有一半长。
width和height是矢量图作为drawable显示在View中显示宽高的比例
按钮效果
如果要做按钮效果,就是按下和抬起颜色不一样,如何实现呢,我们这里在res/color下创建一个selector,配置不一样的颜色
svg_ic_select.xml
然后在使用矢量图时将这个selector指定为tint就行了
path 定义了要被绘制的路径。
android:name 给 path 分配一个唯一的名字,这样在其他地方可以通过名字来引用这个路径,比如说动画
android:pathData 路径信息。
android:fillColor 填充路径的颜色
android:fillAlpha 填充路径的透明度
android:strokeColor 边框颜色
android:strokeWidth 边框粗细尺寸
android:strokeAlpha 边框透明度
android:trimPathStart 从路径起始位置截断路径的比率,取值范围从 0 到 1
android:trimPathEnd 从路径结束位置截断路径的比率,取值范围从 0 到 1
android:trimPathOffset 设置路径截取的范围,取值范围从 0 到 1
android:strokeLineCap 设置路径线帽的形状,取值为 butt, round, square.
android:strokeLineJoin 设置路径交界处的连接方式,取值为 miter,round,bevel.
android:strokeMiterLimit 设置斜角的上限
上面的属性大部分和Paint里的属性差不多
pathData
M = moveto 相当于 android Path 里的moveTo(),用于移动起始点
L = lineto 相当于 android Path 里的lineTo(),用于画线
H = horizontal lineto 用于画水平线
V = vertical lineto 用于画竖直线
C = curveto 相当于cubicTo(),三次贝塞尔曲线
S = smooth curveto 同样三次贝塞尔曲线,更平滑
Q = quadratic Belzier curve quadTo(),二次贝塞尔曲线
T = smooth quadratic Belzier curveto 同样二次贝塞尔曲线,更平滑
A = elliptical Arc 相当于arcTo(),用于画弧
Z = closepath 相当于closeTo(),关闭path
大写表示绝对定位,小写表示相对定位
clip-path 绘制的剪切路径
android:name clip path 的名字
android:pathData 和path的取值一样。
注意,clip-path 只对当前的 group 和子 group 有效
我们看下效果
把我们的矢量图先截取一部分
/>
我们看到,先移动到点(19,3),然后连线到(5,3),然后画三阶贝赛尔曲线,然后画竖线14个像素,最终成像就是上图。接着我们把这个path还原,加上clip-path
看到,path还是那个path,但是被截取了一半
animated-vector 矢量资源动画
定义在res/drawable/中
我们先定义一组svg路径
svg_ic_add_box_animation.xml
代码中定义了三个路径
group 定义了一组路径,主要是用来设置路径做动画的关键属性的
android:name group 的名字,这样在其他地方可以通过名字来引用这个路径,比如说动画
android:rotation 旋转角度
android:pivotX 定义缩放和旋转该 group 时候的 X 参考点。该值相对于 vector 的 viewport 值来指定的。
android:pivotY 定义缩放和旋转该 group 时候的 Y 参考点。该值相对于 vector 的 viewport 值来指定的。
android:scaleX 定义 X 轴的缩放倍数
android:scaleY 定义 Y 轴的缩放倍数
android:translateX 定义移动 X 轴的位移。相对于 vector 的 viewport 值来指定的。
android:translateY 定义移动 Y 轴的位移。相对于 vector 的 viewport 值来指定的。
注意一下,相对于 vector 的 viewport 值来指定这句话的意思,从上面分析中知道,矢量图的路径点是固定的,viewport就是路径点依赖的画布。当矢量图绘制到界面上时,是会有缩放的。所以要以viewport为基准来translate
看到group 的属性,是不是和objectAnimator 很像,我们先定义几个动画objectAnimator
objectAnimator 一个或多个对象动画器
如果要定义动画的集合,则应该在res/anim/中定义,如下
svg_animation_scale.xml
如果是单独的动画,可以在res/animator/中定义,如下
有了动画,就需要把动画和矢量图结合起来,这个时候就需要用到我们的animated-vector
svg_box_animation.xml
这里要注意一下,动画里面的android:propertyName,要与target里对应的path个group对应。如果说这个target对应的name对象是一个path,animation对应的propertyName是一个scaleX。就会抛出异常:
java.lang.IllegalArgumentException: Property: translateY is not supported for FullPath
ok,动画文件有了,加入到布局中
点击启动动画
public void startBoxAnim(View view) {
Drawable drawable = ((ImageView) view).getDrawable();
((AnimatedVectorDrawable)drawable).start();
}
看一下效果
我们看到,动画是有了,不太美观。我们想让红色方框围绕自己圆心转,绿色的球也围绕自己圆心缩放,这时候就需要修改group里的参数了。前面我们知道
android:pivotX 定义缩放和旋转该 group 时候的 X 参考点。该值相对于 vector 的 viewport 值来指定的。
android:pivotY 定义缩放和旋转该 group 时候的 Y 参考点。该值相对于 vector 的 viewport 值来指定的。
先来找红方的圆心,我们的viewport 是72*24,红方的pathData="M27,3L45,3L45,21L27,21z",那么,圆心就是x = 27+(45-27)/2 = 36, y = 3+(21-3)/2 = 12,所以圆心就是(36, 12)
再来看绿圆的圆心,看下pathData="M60,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" ,显然,圆心就是(60,12)
修改代码
看下效果
代码中设置动画
AnimatedVectorDrawableCompat focus = AnimatedVectorDrawableCompat.create(this, R.drawable.svg_searchbar_focus);
view.setImageDrawable(focus);
focus.start();
代码中解析svg文件
我们拿到一个svg文件
china.svg
//后面省略
这是一个中国地图的svg,每个path代表一个省,如何解析这个文件呢
final InputStream inputStream = context.getResources().openRawResource(R.raw.china);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); //取得DocumentBuilderFactory实例
DocumentBuilder builder = null; //从factory获取DocumentBuilder实例
try {
builder = factory.newDocumentBuilder();
Document doc = builder.parse(inputStream); //解析输入流 得到Document实例
Element rootElement = doc.getDocumentElement();
NodeList items = rootElement.getElementsByTagName("path");
List list = new ArrayList<>();
for (int i = 0; i < items.getLength(); i++) {
Element element = (Element) items.item(i);
String pathData = element.getAttribute("android:pathData");
Path path = PathParser.createPathFromPathData(pathData);
RectF rect = new RectF();
path.computeBounds(rect, true);
list.add(proviceItem);
}
itemList = list;
} catch (Exception e) {
e.printStackTrace();
}
PathParser.createPathFromPathData可以将SVG中pathData直接转成我们canvas中需要用到的path
path.computeBounds(rect, true);这段代码可以求出不规则path的边界范围
最后将path绘制到界面上