本文是以读者对SVG有一定了解为前提的,否则请先百(谷)度(歌)了解下。
实践都是从坑里爬出来的,因此本文的子题目也可叫做Android使用矢量图填坑记。
文章开始前,先墙裂安利一个网站,阿里的iconfont,海量在线矢量图,早收藏早致富。本文主要涉及到的矢量图资源均来自该网站。放图镇楼:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="8dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
vector>
基本属性说明:
width
,height
:图片的宽高。可手动修改到需要尺寸;viewportHeight
,viewportWidth
:对应将上面height
width
等分的份数。以svg_ic_arrow_right.xml
举例,可以想象将长宽都为8dp的正方形均分为24x24的网格,在这个网格中就可以很方便地描述点的坐标,图像就是这些点连接起来构成的。fillColor
:填充颜色。最好直接在这里写明色值#xxxxxxxx
,而不要用@color/some_color
的形式,避免某些5.0以下机型可能会报错。pathData
:在2中描述的网格中作画的路径。具体语法不是本文的重点,故不展开。
这段代码描述出来的是一个白色箭头,可以从Android Studio的preview功能栏里预览到它的样子:
android{
...
defaultConfig {
...
vectorDrawables.useSupportLibrary = true
}
...
}
dependencies {
...
compile "com.android.support:appcompat-v7:21+" // 至少Api21
...
}
项目的Activity
中都包含(通用做法是在BaseActivity中加):
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
这是继承自ImageView
用于5.0以下加载矢量图的控件,只需要替换src
为srcCompat
属性,其它没什么不同。例如:
<android.support.v7.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/svg_ic_arrow_right"/>
- 如果你的
Activity
直接或间接继承自AppCompatActivity
,当前视图中的ImageView
在编译过程中会被自动转为AppCompatImageView
(support包中所有含有AppCompat前缀的控件均受相同处理),因而在Activity
中通过findViewById()
的实例用ImageView
或AppCompatActivity
接收是没有区别的。- 用以上条件的
Activity
中装载的Fragment
,或者通过动态注入(如Dialog
的contentView
)的ImageView
,均将被自动转为AppCompatActivity
。- 从xml文件中初始化
ImageView
并加载矢量图,必须使用AppCompatImageView
的srcCompat
属性。ImageView
的染色属性tint
同样适合矢量图。
在我的经验中,TextView
可以用到矢量图的场景是最多的,主要是设置CompoundDrawable
。例如:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableRight="@drawable/svg_ic_arrow_right"
android:drawablePadding="4dp"
android:text="drawable right"/>
这样设置后,没有任何不适,编译器也不报错,可能你自己运行也没问题。但是!这才是深坑啊。5.0以下某些机型可能会崩溃的。
AppCompatTextView
是没有对CompoundDrawable
进行适配的,所以需要自己动手才能丰衣足食。简单原理是,判断系统版本如果小于5.0,就用ContextCompat.getDrawable
获取到Drawable
实例,再setCompoundDrawablesWithIntrinsicBounds
。
这个部分本人已经做好并开源了,地址:VectorCompatTextView,轻松compile到项目中使用。还特意添加了一个实用功能——tint
染色——可以选择是否让图标与文字颜色一样,这样就不必关心xml里的fillColor
属性了。用例:
<com.xw.repo.VectorCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_gray_light"
android:gravity="center_vertical"
android:padding="16dp"
android:text="Next"
android:textSize="16sp"
app:drawableRightCompat="@drawable/svg_ic_arrow_right"
app:tintDrawableInTextColor="true"/>
效果:
Tips:
存在这样一种情形,连续两个控件(编号1和2)都来加载同一张svg图,svg的本来颜色是A,控件1将颜色改为了B,控件2也加载这张svg,颜色却变为了B,这应该是Android系统对矢量图的缓存造成的。解决办法是把svg的本来颜色改为B,控件2再改为颜色A。(好绕,不过一般不会遇到)
MenuItem
就是在res/menu/
目录下通过xml配置的菜单,适用于NavigationView
的menu
属性和Activity
中onCreateOptionsMenu()
注入的选项菜单。
前一阵做了一个小应用叫“简影讯”,发现MenuItem
是可以完美支持矢量图的,而且也可以自动跟随文字颜色改变颜色。且看证明:
简影讯(开源地址:GracefulMovies),是一枚基于Retrofit+RxJava+MVP+Colorful多彩主题框架开发的高颜值影讯app。简约,优雅,精彩,即看即走。欢迎在应用宝或360手机助手下载围观。
该项目除ic_launcher
外,所有的图标都是矢量图。
“是时候全面使用矢量图了。”
自定义View中也可以自由使用矢量图。
首先需要将VectorDrawable 转为 Bitmap,看码:
public Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
if (Build.VERSION.SDK_INT drawable = (DrawableCompat.wrap(drawable)).mutate();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
执行以上方法获得一个Bitmap的实例(设为mVectorBitmap),然后再在ondraw()
里根据你的需求画出bitmap:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
///
canvas.drawBitmap(mVectorBitmap, left, top, paint);
///
}
矢量图的优点一大把,但也不是万能的。矢量图特别适合icon图标的应用场景,但是不能用于比如加载相册时,设置的placeholder或者error这类需要频繁切换回收的应用场景,否则会造成非常明显的卡顿,因为矢量图是不被硬件加速支持的。
Android 5.0推出已经有些年份了,也不知道Android开发圈对矢量图的使用情况,但知道比如微信这些大厂早已全面推广使用。然而在本人周边似乎自己算先驱了,所以才有了把过程中的一些经验总结分享出来的想法。
毕竟本人才疏学浅,难免有纰漏之处,请大神轻拍砖,并不吝赐教。若对后来学习者有帮助,那花这一天码的字自然也超值了,希望共勉。