简介
从Lollipop(Android 5.0)开始, Android引入了对矢量图的支持, 但并不支持svg这种矢量图片格式, 而是以VectorDrawable的方式来实现矢量图的效果。 VectorDrawable也是Drawable的一个直接子类, 像其它Drawable那样通常情况下是在xml中定义, 它对应的xml标签是<vector/>, 基本结构如下:
上图表示的就是一个Android小机器人,具体效果看下面的效果图。其中有以下几点需要注意
- vector是VectorDrawable对应的根标签
android:width与android:height对应矢量图的实际大小(矢量图是可以无限大, 但通常情况下一个图片都应该有一个原始大小, 假如你将此VectorDrawable作为一个ImageView的src, ImageView的大小都设置为wrap_content, 则ImageView对应的实际大小就是这里设置的大小)- android:viewportWidth与android:viewportHeight是指当前Drawable对应的虚拟Canvas的大小, 之所以说是虚拟的是因为实际上并不存在这样一个Canvas, 又之所以需要这个值是因为在<path/>标签中的路径数据要基于具体的坐标系来绘制
- <path/>标签对应路径信息, 这里的path与我们自定义绘制图形时用的Path原理一样, 就是记录一些绘图操作, 具体对应其中的pathData.PathData中对应的路径描述符号不需要我们去记, 通常情况下由绘图软件生成svg图片再从svg文件中提取.
如何使用
在Android5.0及以上版本中,使用向量图和普通的drawable资源并没有什么差别,如下
由上可知,VectorDrawable不仅支持普通的src属性,还支持tint、background、drawableStart
最低Android版本4.3 最高版本7.1,在Android4.4设备上运行以上代码代码发现可以正常运行不会出现crash,VectorDrawable明明是Android5.0以后才支持的,为什么在4.4上也可以正常运行呐,查找资料才知道,当minSdkVersion小于5.0时,gradle编译的时候会把VectorDrawable生成相应的位图资源打包在APP里(VectorDrawable并不会打包在app里),运行时都会引用位图资源。当minSdkVersion大于等于5.0时会直接引用VectorDrawable。反编译App查看生成位图的位置,其包含在mdpi,hdpi,xhdpi,xxhdpi,xxxhdpi中,当然也可以指定生成特定分辨率的位图资源,需要在gradle文件中配置,如下
defaultConfig {
vectorDrawables.generatedDensities = ['hdpi','xxhdpi']
}
这样在minSdkVersion小于5.0会存在一个问题,就是app中包含不同分辨率的位图资源这会导致app体积增大。有没有什么办法可以让低版本设备直接支持VectorDrawable而不是生产响应的位图资源呐,答案是肯定的。
在低版本中支持向量图
- 配置使用SupportLibrary
首先需要在你的build.gradle配置文件中增加如下配置
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
上面的配置的作用是强制gradle在编译时不自动生成兼容低版本的位图资源.
- 添加SupportLibrary依赖
其次需要添加23.2.0 以后的Support Library,如下
compile 'com.android.support:appcompat-v7:23.2.0'
这里有一个坑,就是在23.2.0中首次发布了这个功能,随后发现在内存和配置更新中存在问题,于是就在23.3.0中去除了这个功能,在随后的一个版本23.4.0中又修复了这个问题,但是需要在代码中手动设置一个flag。原文如下:
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.
所以应添加23.4.0及以上的Support依赖
compile 'com.android.support:appcompat-v7:23.4.0'
并在Activity的前面添加flag,开启该功能
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
- 代码引用
最后就是如何使用了,ImageView在引用VectorDrawable资源时使用 app:srcCompat 取代 android:src,或者使用android:src引用一个selector资源;若想在background中使用Vectordrawable资源用作View的背景,并没有一个类似app:backgroundCompat方法,这里可以使用selector包裹一层,当然也可以在代码中设置
Resources resources = context.getResources(Resources, int, Theme);
Theme theme = context.getTheme();
Drawable drawable = VectorDrawableCompat.create(resources, R.drawable.vector_drawable, theme);
view.setBackground(drawable);
注:上面代码只是明确知道你所要加载的resource是VectoreDrable,若是在不知道自己加载是普通的位图资源还是矢量图,上面写的就会抛出NotFoundException异常。这也很好理解,当你要创建矢量图的时候发现取到的资源并不是矢量图而是普通的位图资源,所以就会抛出异常。我在VectorDrawableCompat中并没有找到判断drawable id是否为矢量的方法,不过这里有一个取巧的办法,那就是捕获异常,如下
Drawable drawable ;
try {
drawable = ContextCompat.getDrawable(context, R.drawable.icon);
} catch (Resources.NotFoundException e) {
drawable = VectorDrawableCompat.create(context, item.icon, context.getTheme()) ;
}
若有更好的办法,请提出来。
Show me code
首先是布局文件,如下
其中包含的selector资源如下
Activity代码如下
public class MainActivity extends AppCompatActivity {
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Drawable drawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_android_black_24dp, getTheme());
findViewById(R.id.view).setBackground(drawable);
}
}
以上效果和上图一样
reference: developer.android.com/studio/writ… medium.com/@chrisbanes… www.jianshu.com/p/e3614e7ab…