Android Vector曲折的匹配之路

Android Vector曲折的匹配之路

两年前写书的时候,就在探讨Android
L提出的Vector,可切磋下来发现,完全不富有包容性,相信那也是它没有被广大应用的一个缘故,经过谷歌(Google)的不懈努力,现在Vector终于迎来了它的青春。

4.jpg

在篇章后边,会给出本文的Demo和功力图,并开源在Github

Vector Drawable

Android 5.0发表的时候,Google提供了Vector的支撑。Vector
Drawable相对于一般的Drawable来说,有以下几个便宜:

  • Vector图像可以活动举行适配,不必要通过分辨率来安装不一致的图纸
  • Vector图像可以大幅收缩图像的体积,同样一张图,用Vector来促成,可能只有PNG的几非常之一
  • 利用简易,很多统筹工具,都得以向来导出SVG图像,从而转换成Vector图像
  • 作用强大,不用写过多代码就可以完成格外复杂的动画
  • 干练、稳定,前端已经不行广阔的拓展利用了

Vector图像刚发表的时候,是只帮忙Android 5.0+的,对于Android
pre-L的连串来说,并不可能动用,所以,可以说那时候的Vector并从未什么样卵用。可是自从AppCompat
23.2过后,谷歌(Google)对p-View的Android系统也进行了合营,也就是说,Vector可以运用于Android
2.1上述的富有系统,只必要引用com.android.support:appcompat-v7:23.2.0以上的版本就足以了,那时候,Vector应该算是迎来了它的青春。

什么收获Vector图像

概念

率先,需求讲解多少个概念——SVG和Vector。

SVG,即Scalable Vector Graphics
矢量图,那种图像格式在前者中早就运用的老大普遍了,详见WIKI:https://en.wikipedia.org/wiki/Scalable\_Vector\_Graphics

Vector,在Android中指的是Vector
Drawable,也就是Android中的矢量图,详见:https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html

因而,可以说Vector就是Android中的SVG完成,因为Android中的Vector并不是支撑一切的SVG语法,也尚未须求,因为全部的SVG语法是相当复杂的,但已经援助的SVG语法已经丰盛了,越发是Path语法,大致是Android中Vector的标配,详细可以参照:http://www.w3.org/TR/SVG/paths.html

Vector语法简介

Android以一种简化的点子对SVG进行了合营,那种方法就是因而拔取它的Path标签,通过Path标签,大致可以已毕SVG中的其它具有标签,纵然可能会复杂一点,但这几个事物都是可以由此工具来成功的,所以,不用操心写起来会很复杂。

Path指令解析如下所示:

  1. 帮助的授命:
  • M = moveto(M X,Y) :将画笔移动到指定的坐标地点
  • L = lineto(L X,Y) :画直线到指定的坐标地方
  • H = horizontal lineto(H X):画水平线到指定的X坐标地方
  • V = vertical lineto(V Y):画垂直线到指定的Y坐标地方
  • C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):四回贝赛曲线
  • S = smooth curveto(S X2,Y2,ENDX,ENDY)
  • Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次贝赛曲线
  • T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射
  • A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线
  • 2018正版葡京赌侠诗,Z = closepath():关闭路径
  1. 选拔条件:
  • 坐标轴为以(0,0)为着力,X轴水平向右,Y轴水平向下
  • 怀有指令大小写均可。大写相对定位,参照全局坐标系;小写相对固定,参照父容器坐标系
  • 指令和数据间的空格可以大约
  • 如出一辙指令出现反复得以只用一个

留神,’M’处理时,只是活动了画笔, 没有画其余事物。
它也得以在后面给出上还要绘制不总是线。

关于这么些语法,开发者要求的并不是成套贯通,而是可以看懂即可,其他的都足以交给工具来落实。

从PNG到SVG

  • 设计师

要从一般采用的PNG图像转换来SVG图像,对于设计师来说,并不是一件难事,因为大多数的筹划工具(PS、Illustrator等等)都帮忙导出种种格式的图像,如PNG、JPG,当然,也席卷SVG,由此,设计师可以完全按照原来的方法开展统筹,只是最终导出的时候,选拔SVG即可。

  • 程序员

不必要开发者都去上学应用那些规划工具,开发者可以使用部分工具,自己转换一些相比基础的图像,http://inloop.github.io/svg2android/
就是那般一个不行牛逼的网站,可以在线将一般性图像转换为Android Vector
Drawable。如图所示:

5.png

要么,还足以采纳SVG的编辑器来开展SVG图像的编辑,例如http://editor.method.ac/

6.png

使用Android Studio

选择Android Studio的Vector
Asset,可以格外有利于的开创Vector图像,甚至可以直接通过当地的SVG图像来生成Vector图像,如图所示:

2.png

跻身未来,就可以生成Vector图像,如图所示:

3.png

谷歌的极度之路

只兼容L+

Vector是在Android L中提议来的新定义,所以在刚初始的时候是只包容L+的。

Gradle Plugin 1.5的兼容

从Gradle Plugin 1.5上马,谷歌支持了一种包容格局,即在Android
L之上,使用Vector,而在L之下,则采取Gradle将Vector生成PNG图像。

Android gradle plugin
1.5发表之后,加入了一个跟VectorDrawable有关的新职能。Android build
tools
提供了其余一种缓解包容性的方案,假诺编译的本子是5.0事先的本子,那么build
tools
会把VectorDrawable生成对应的png图片,那样在5.0以下的本子则选择的是生成的png图,而在5.0上述的版本中则使用VectorDrawable.在build.gradle添加generatedDensities配置,可以安插生成的png图片的密度。

AppCompat23.2的兼容

从AppCompat23.2先导,谷歌(Google)起首接济在低版本上行使Vector。

静态Vector图像

我们有诸多形式可以得到那一个Vector,那么哪些使用它们啊,Android
5.0之上的选用就不讲了,不太具有广泛代表性,大家从pre-L版本的相当初阶做起。

pre-L版本包容

VectorDrawableCompat着重于AAPT的一部分成效,它能保全近期矢量图使用的拉长的性能ID,以便他们得以被pre-L版本之前的引用。

在Android
5.0事先使用Vector,须要aapt来对资源实行部分拍卖,这一进度可以在aapt的配备中开展安装,即使没有启用那样一个flag,那么在5.0以下的装备上运行就会生出android.content.res.Resources$NotFoundException。

先是,你要求在类型的build.gradle脚本中,增添对Vector包容性的支撑,代码如下所示:

使用Gradle Plugin 2.0以上:

android {

    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

使用Gradle Plugin 2.0以下,Gradle Plugin 1.5以上:

android {
  defaultConfig {
    // Stops the Gradle plugin’s automatic rasterization of vectors
    generatedDensities = []
  }
  // Flag to tell aapt to keep the attribute ids around
  aaptOptions {
    additionalParameters "--no-version-vectors"
  }
}

像后边提到的,那种包容格局实际上是先关闭AAPT对pre-L版本选用Vector的折衷,即在L版本以上,使用Vector,而在pre-L版本上,使用Gradle生成相应的PNG图片,generatedDensities这些数组,实际上固然要生成PNG的图片分辨率的数组,使用appcompat后就不须求那样了。

本来,最要害的或者添加appcompat的支撑:

compile 'com.android.support:appcompat-v7:23.4.0'

与此同时,确保您使用的是AppCompatActivity而不是无独有偶的Activity。

Vector图像

一个中央的Vector图像,实际上也是一个xml文件,如下所示:

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

    <path
        android:name="square"
        android:fillColor="#000000"
        android:pathData="M100,100 L400,100 L400,400 L100,400 z"/>

</vector>

显示如图所示:

7.png

那里须要解释下那里的多少个标签:

  • android:width \ android:height:定义图片的宽高
  • android:viewportHeight \
    android:viewportWidth:定义图像被划分的比例大小,例如例子中的500,即把200dp大小的图像划分成500份,后边Path标签中的坐标,就满门接纳的是那里划分后的坐标种类。

诸如此类做有一个不行好的效果,就是将图像大小与图像分离,前面可以任意修改图像大小,而不需求修改PathData中的坐标。

  • android:fillColor:PathData中的那一个属性就不详细讲了,与Canvas绘图的性质基本类似。

在控件中应用

有了静态的Vector图像,就可以在控件中运用了。

可以发现,这里我们选用的都是普普通通的ImageView,好像并不是AppcomatImageView,那是因为运用了Appcomat后,系统会自行把ImageView转换为AppcomatImageView。

ImageView\ImageButton

对于ImageView那样的控件,要同盟Vector图像,只需求将此前的android:src属性,换成app:srcCompat即可,示例代码如下所示:

<ImageView
    android:id="@+id/iv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/vector_image"/>

在代码中装置的话,代码如下所示:

ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageResource(R.drawable.vector_image);

setBackgroundResource也是足以安装Vector的API

Button

Button并无法一向利用app:srcCompat来行使Vector图像,需要经过Selector来进展应用,首先,创立多个图像,用于Selector的多少个情形,代码如下所示:

selector1.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M14.59,8L12,10.59 9.41,8 8,9.41 10.59,12 8,14.59 9.41,16 12,13.41 14.59,16 16,14.59 13.41,12 16,9.41 14.59,8zM12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
</vector>

selector2.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
</vector>

selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/selector1" android:state_pressed="true"/>
    <item android:drawable="@drawable/selector2"/>
</selector>

万分容易,只是把常备的Selector中的图像换成了Vector图像而已,接下去,在Button中利用那些Selector即可:

<Button
    android:id="@+id/btn"
    android:layout_width="70dp"
    android:layout_height="70dp"
    android:background="@drawable/selector"/>

接下来运行,假设你觉得可以运作,那就是太天真了,都说了是相当,怎么能没有坑呢,那里就是一个坑……

其一坑实际上是有历史渊源的,谷歌(Google)的一位开发者在博客中写到:

First up, this functionality was originally released in 23.2.0, but
then we found some memory usage and Configuration updating issues so
we it removed in 23.3.0. In 23.4.0 (technically a fix release) we’ve
re-added the same functionality but behind a flag which you need to
manually enable.

实则,他们的这一个改变,就影响了如同DrawableContainers(DrawableContainers
which reference other drawables resources which contain only a vector
resource)那样的类,它的一个超人,就是Selector(StateListDrawable也是)。那么些开发者在文中提到的flag,就是底下的那段代码,放在Activity的先头就足以了:

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

开启这一个flag后,你就足以健康使用Selector那样的DrawableContainers了。同时,你还开启了接近android:drawableLeft那样的compound
drawable的运用权限,以及RadioButton的行使权力,以及ImageView’s src属性。

RadioButton

RadioButton的Button同样可以定义,代码如下所示:

<RadioButton
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:button="@drawable/selector"/>

动态Vector基础

动态Vector才是Android Vector Drawable的出色所在

动态的Vector须要经过animated-vector标签来进展落到实处,它就好像一个粘合剂,将控件与Vector图像粘合在了一起,一个基础的animated-vector代码如下所示:

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

    <target
        android:name="left"
        android:animation="@animator/XXXXX2"/>

</animated-vector>

骨子里那个中唯有四个根本是须要关爱的,XXXXX1和XXXXX2。一个实际的示范如下所示:

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

    <target
        android:name="left"
        android:animation="@animator/anim_left"/>

    <target
        android:name="right"
        android:animation="@animator/anim_right"/>

</animated-vector>

那里表示目的图像是drawable/ic_arrow,对left、right分别使用了anim_left、anim_right动画。那里的name属性,就是在静态Vector图像中group或者path标签的name属性。

animated-vector标签在明日的Android
Studio中实际上是会报错的,但以此并不影响编译和运作,属于Android
Studio的Bug。

目的图像

XXXXX1是目标Vector图像,也就是静态的Vector图像,例如:

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

    <group android:name="left">
        <path
            android:fillColor="#FF000000"
            android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3"/>
    </group>

    <group android:name="right">
        <path
            android:fillColor="#FF000000"
            android:pathData="M14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4"/>
    </group>

</vector>

可以发现,那里的Vector图像比此前大家看见的要多了一个group标签。group标签的法力有三个:

  • 对Path进行分组,由于大家前面须求针对Path举办动画,所以可以让具有同样动画效果的Path在同一个Group中
  • 举办动画效果,单个的path标签是绝非translateX和translateY属性的,因而不能利用性能动画来决定path
    translateY,而group标签是一对,所以大家需求先将相关的path标签元素包裹在一个个的group标签中.

动画效果

XXXXX2实际上就是模板要落成的动画,动画效果实在就是基础的特性动画,例如:

anim_left.xml

<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/anticipate_overshoot"
    android:propertyName="translateX"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:valueFrom="0"
    android:valueTo="-10"
    android:valueType="floatType"/>

anim_right.xml

<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/anticipate_overshoot"
    android:propertyName="translateX"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:valueFrom="0"
    android:valueTo="10"
    android:valueType="floatType"/>

在代码中采取

ImageView imageView = (ImageView) findViewById(R.id.iv);
AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(
        this, R.drawable.square_anim
);
imageView.setImageDrawable(animatedVectorDrawableCompat);
((Animatable) imageView.getDrawable()).start();

动态Vector包容性问题

向下兼容问题

一说到包容,就只好涉及坑,大概拥有的为了合营而做的变动,都会留下一些不可填满的坑,动态Vector动画也不例外,纵然谷歌(Google)已经对Vector图像举行了Android
2.1之上的卓殊,但对于动态Vector动画,依然有过多限制的,例如:

  • Path Morphing,即路径变换动画,在Android pre-L版本下是无能为力选拔的。
  • Path Interpolation,即路径插值器,在Android
    pre-L版本只好使用系统的插值器,不可以自定义。
  • Path
    Animation,即路径动画,那么些一般采纳贝塞尔曲线来顶替,所以没有太大影响。

进化包容问题

除却在低版本上的包容性问题,在L版本上述,也设有包容性问题,即持续了AppCompatActivity的界面,即使一直设置ImageView的srcCompat,那么Path
Morphing动画是心有余而力不足生效的,因为默许的AppCompatActivity已经默许使用ImageViewCompat给转换了,可是AnimatedVectorDrawableCompat是不辅助Path
Morphing动画的,所以,在AppCompatActivity界面里面就不算了。

解决办法很粗略,即利用代码来给ImageView添加动画:

ImageView imageView = (ImageView) view;
AnimatedVectorDrawable morphing = (AnimatedVectorDrawable) getDrawable(morphing);
imageView.setImageDrawable(morphing);
if (morphing != null) {
    morphing.start();
}

留神不要选取AnimatedVectorDrawableCompat即可。

抽取string包容问题

开发者有时候为了代码简洁可能会把Vector图像中的pathData放到string.xml中,然后在Vector图像中援引string。

但那种方法借使由此生成png来合营5.0之下机型的话,会报pathData错误,编译器不会去读取string.xml,只好把pathData写到Vector图像中,动画文件中也是平等,这也是为着合营做出的自我就义呢,不得而知。

其余包容问题

其他极度想获得、诡异、无法了然的包容性问题,只能够通过版本文件夹的不二法门来开展包容了,例如drawable-v21和drawable,分别成立三个公文名相同的资源在三个公文夹下,那样在21以上版本,会接纳drawable-v21的资源,而任何会使用drawable下的资源。

动态Vector进阶

用好ObjectAnimator

所谓Vector动画进阶,实际上就是在拔取ObjectAnimator的部分性质,越发是trimPathStart、trimPathEnd那多个针对Vector的习性(要留意pathData属性不匹配pre-L)。

那五个特性的法定文档如下所示:

android:trimPathStart
The fraction of the path to trim from the start, in the range from 0 to 1.
android:trimPathEnd
The fraction of the path to trim from the end, in the range from 0 to 1.
android:trimPathOffset
Shift trim region (allows showed region to include the start and end), in the range from 0 to 1.

事实上很简短,就是一个图像的截取,设置一个百分比即可,即眼前绘制多少比例的图像,其他部分不绘制,Start和End分别就是从PathData的Start和End先河算,大家参考多少个例证就能知晓了。

理解Path Morph

Path
Morph动画是Vector动画的一个高级应用,说到底,也就是四个PathData的转换,然而那种转移并不是随心所欲的,对于四个PathData,它们能拓展Path
Morph的前提是,它们拥有同等个数的关键点,即四个途径的变换,只是关键点的坐标变化,领悟了那个基本原理,完毕Path
Morph就格外简单了。

学习Vector

在Github上自家开源了一个Vector的动画Demo库,地址如下所示:

https://github.com/xuyisheng/VectorDemo

其一Demo分为两片段,一部分是足以包容Android
pre-L版本和L+版本的Vector动画,另一部分(通过Actionbar的按钮切换)是不得不包容L+的Vector动画。

种种Vector动画,基本都包罗四部分情节,即:

  • Vector:图像资源
  • Animated-vector:动画、图像粘合剂
  • ObjectAnimator:动画资源
  • 代码:启动动画

各种Vector动画通过这七个部分去开展解析,就格外清晰了。

那里展现下Demo的作用图:

vector.gif

Vector性能问题

有读者在篇章后边留言,询问VectorDrawable的性能问题,那里解释一下。

  1. Bitmap的绘图功用并不一定会比Vector高,它们有早晚的平衡点,当Vector相比较简单时,其功用是自然比Bitmap高的,所以,为了确保Vector的高功效,Vector要求进一步简约,PathData更加规范、精简,当Vector图像变得分外复杂时,就必要动用Bitmap来代替了
  2. Vector适用于ICON、Button、ImageView的图标等小的ICON,或者是急需的动画片效果,由于Bitmap在GPU中有缓存成效,而Vector并从未,所以Vector图像无法做往往的重绘
  3. Vector图像过于复杂时,不仅仅要专注绘制成效,开端化效能也是须求考虑的根本因素
  4. SVG加载速度会快于PNG,但渲染速度会慢于PNG,毕竟PNG有硬件加速,但平均下来,加载速度的升级弥补了绘图的速度缺陷。
    谷歌(Google)的那几个视频中,已经对Vector的频率问题做了表明,可以参照下:

https://www.youtube.com/watch?v=wlFVIIstKmA&feature=youtu.be&t=6m3s

参考

https://medium.com/@shemag8/animated-vector-drawable-e4d7743d372c\#.3vkt12j20
https://github.com/jpuderer/AnimatedButton

相关文章