目录
前言
作为一名安卓初学者,每次都会在导入应用图标时有一个很大的疑问,如图:
这是一个IDE自带的资源图片,只不过这张图是IDE根据由以下配置文件生成的:
<vector
android:height="24dp"
android:tint="@color/bg_grey"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white"
android:pathData="M11.67,3.87L9.9,2.1 0,12l9.9,9.9 1.77,-1.77L3.54,12z"/>
</vector>
这张图有一个优点——具有“矢量性”,简单来说就是放大后图片不会糊。我的问题是:IDE是根据什么规则来读取这个xml配置文件来生成这么一张图的。当然,我也想由此试着画一些自己的图标。所以以下是我的学习笔记,也可以为读者做一个简单的教程参考。
在阅读教程时,读者应该具有以下基本知识:
- 了解什么是xml文件
- 了解xml的基本语法
- 有面向对象编程的基础(可选)
一、什么是矢量绘制对象(VectorDrawable)
本节是可选的阅读内容,不想阅读的读者可以跳过。本节介绍安卓中矢量绘制对象的基本概念,这个概念有助于读者了解在安卓开发中可以通过什么方式来定义的一个矢量绘制对象,并明白矢量绘制图形相对于传统的图形(例如一张普通的png图像)有什么优势。
首先介绍官方的文档地址:VectorDrawable Object。以下内容实际是官网介绍延伸。
VectorDrawable
即矢量绘制对象代表了一个矢量图形,我们可以通过在XML文件中配置各种图形的信息例如长度、颜色等来定义一个VectorDrawable
对象,它可以是由一组点、线以及各种颜色组成的矢量图形,创建一个矢量图形的过程就叫做矢量绘制。使用矢量绘制来生成图形的主要优点是图形的可伸缩性。
矢量图形可以在不损失图形显示质量的同时进行伸缩,这意味着同一文件的图像大小可根据不同的屏幕像素密度调整,同时还不会损失图像质量。这样一来APK文件可以变得更小,而且开发人员可以减小维护的工作量。最后安卓支持开发者通过使用多个XML文件对应的矢量图像进行实现动画效果,从而告别使用对应多个分辨率的多个图像来实现动画的繁琐操作。
二、如何在XML文件中定义一个VectorDrawable
本节介绍了如何在XML文件中定义一个VectorDrawable对象,也就是在安卓中用XML文件定义一个矢量图形时的相应标签规则。本文只会介绍基本的标签使用规范,而对于其他原理问题则不是本文要考虑的范围。
2.1 基本标签格式
安卓中, 一个VectorDrawable
可以在XML中用 <vector>
标签元素定义。基本的标签框架格式如下:
<!-- vector为主标签,代表了一个矢量图形,
clip-Path、group、path为vector子标签,声明了图形的详细属性 -->
<vector>
<group />
<path />
<clip-path />
</vector>
如图为三个子标签的层次结构的一种示例:
三个子标签可以继续层层嵌套,嵌套的规则为:以group
为一个新的嵌套组,且只能在group
标签里面继续嵌入子标签。group
用于直接表示一组路径或者用于嵌套另一个group
集合,path
、clip-path
标签用于直接说明矢量图形的其他各种属性。用一颗树来描述就是:group
标签只能为非叶结点,path
、clip-path
只能为叶节点。
2.2 主标签vector
<vector
android:name="矢量图形的名称,可选,即非必须属性"
android:width="矢量图形的宽度"
android:height="矢量图形的高度"
android:viewportWidth="视窗宽度,即在预览矢量图形时,预览的空间宽度"
android:viewportHeight="视窗高度,即在预览矢量图形时,预览的空间高度"
android:tint="图形颜色的色调,默认值为空,可选"
android:tintMode="色调的Porter-Duff混合模式,默认值为src_in,可选"
android:autoMirrored="指明当布局方向为RTL(从右到左)时,是否需要镜像复制可绘制对象,默认值为false,可选"
android:alpha="不透明度,默认为1.0,可选">
</vector>
除了width
、height
、viewportWidth
、viewportHeight
四个为必需属性,其余均为可选属性。这里需要强调一下什么是视图窗口(viewport)(注:正确的翻译应该叫视口,但是我觉得这个翻译太拗口且不像是一个正常的汉语表达,所以我按照实际应用效果将其翻译为视图窗口,简称为视窗,听起来顺耳一点),如下图:
图片中的人为观察者,视图窗口就是图中的窗户,视窗大小就是窗户的大小,外面的树就是被观察的对象。视窗大小与位置并不影响外面树的大小,但是会影响观察者的视野,有可能外面的树很高大,但是我们什么也看不见。所以上面的width
、height
、viewportWidth
、viewportHeight
四个属性就是分别用于设置被观察者的宽高与视窗的宽高。在实际开发中,视窗大小默认就是用户的手机屏幕大小,当用户放大画面或缩小画面时,实际是在改变视窗大小(但是手机物理屏幕大小仍未改变),使得被观察者看起来变大或者缩小了(另外用户也可以通过滚轮滑动看到“藏”在当前视窗范围外的被观察者)。
2.3 子标签
2.3.1 group子标签
group
标签用于直接定义一组路径或作为另一个子组的父标签。group
标签可定义位于该标签下整体的变换信息。变换是在与viewport
相同的坐标中定义。应用变换时,按照缩放、旋转和平移的顺序。该标签为可选标签,如果你不需要定义多个组或者对组进行整体操作,则也不需要使用group标签。
<group
android:name="组的名字"
android:rotation="组旋转的角度,默认为0,可选"
android:pivotX="组的缩放、旋转的轴的X坐标。这是在viewport空间中定义的,即以视窗大小为基准,其中图形x、y坐标的起点(0,0)与视窗的最左上角重合,默认值为0,可选"
android:pivotY="组的缩放、旋转的轴的Y坐标。这是在viewport空间中定义的,默认值为0,可选"
android:scaleX="X坐标上的比例量,默认值为1,可选"
android:scaleY="Y坐标上的缩放量,默认值为1,可选"
android:translateX="X坐标上的平移量。这是在viewport空间中定义的,默认值为0,可选"
android:translateY="Y坐标上的平移量。这是在viewport空间中定义的,默认值为0,可选"
>
</group>
group
标签属性作用于该标签下的整体。
2.3.2 path子标签
path
标签用于定义绘制的路径。
<path
android:name="path的名字"
android:pathData="使用与SVG路径数据中的“d”属性完全相同的格式定义路径数据。这是在viewport空间中定义的"
android:fillColor="指定用于填充路径的颜色。可以是颜色,或者对于SDK 24+,可以是颜色状态列表或渐变色。如果此属性已设置动画,则动画设置的任何值都将覆盖原始值,如果未指定此属性,则不会绘制路径填充,可选"
android:strokeColor="指定用于绘制路径轮廓的颜色。可以是颜色,或者对于SDK 24+,可以是颜色状态列表或渐变色。如果此属性已设置动画,则动画设置的任何值都将覆盖原始值,如果未指定此属性,则不会绘制路径轮廓"
android:strokeWidth="路径画线的宽度。默认值为0,可选"
android:strokeAlpha="路径画线的不透明度。默认值为1,可选"
android:fillAlpha="路径填充的不透明度。默认值为1,可选"
android:trimPathStart="截掉从起点到某个位置的部分,保留剩下的部分,范围从0到1。默认值为0,可选"
android:trimPathEnd="截掉从某个位置到终点的部分,保留剩下的部分,范围从0到1。默认值为1,可选"
android:trimPathOffset="在截取path的时候设置path原点的位置,范围从0到1,与上两个属性一起使用。默认值为0,可选"
android:strokeLineCap="设置线的末端形状,butt,round,square。默认为butt,可选"
android:strokeLineJoin="设置线的连接处形状,miter,round,bevel。默认为miter,可选"
android:strokeMiterLimit="设置路径画线的miter形状限制。默认值为4,可选"
android:fillType="对于SDK 24+,意为设置路径的填充类型。类型可以是“evenOdd”或“nonZero”。它们的行为与SVG的“填充规则”属性相同。默认值为非零,可选"
/>
这里有一个很重要的属性即第二个属性pathData
,不了解这个属性值的规则将导致无法绘制出一个图形,这个属性相当于规定了画笔路径,具体规则见其他博主的博文,例如这篇:Android SVG的pathData详解。
2.3.3 clip-path标签
clip-path
标签用于剪切path
标签中定义的图形,所以clip-path必须和path
配套使用,否则没有意义。同时需要注意,剪切路径操作仅适用于当前组及其子组,即定义在group
下面的组或者子组,不会对上一级生效。
<clip-path
android:name="clip-path的名字"
android:pathData="使用与SVG路径数据中的“d”属性相同的格式定义剪切路径"
/>
三、绘制示例
示例XML文件:
<!-- 此xml文件定义了一个由三个相对坐标点(0,0)、(100,0)、(0,100)围城的封闭三角形
,视图窗口为600x600 -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp"
android:width="64dp"
android:viewportHeight="600"
android:viewportWidth="600" >
<!-- pivotX、pivotY定义了图形绘制后相对于视窗的X、Y轴偏移量 -->
<!-- rotation=45.0,即将图形以顺时针旋转45度 -->
<group
android:name="rotationGroup"
android:pivotX="000.0"
android:pivotY="000.0"
android:rotation="45.0" >
<path
android:name="origin"
android:fillColor="@color/bg_white"
android:pathData="M100,100 l 0,0 100,0 0,100" />
</group>
</vector>
效果图: