Android矢量圖I--VectorDrawable基礎(chǔ)介紹了VectorDrawable的用法及其常用屬性,掌握上篇文章的基礎(chǔ)知識(shí)差不多就能在項(xiàng)目中使用矢量圖應(yīng)對(duì)一些基本的需求開發(fā)了。這篇文章介紹其余的全部屬性,其中的一些屬性在實(shí)際開發(fā)中可能用到的比較少。
vector屬性
-
android:alpha:矢量圖的透明度,范圍0-1,默認(rèn)1; -
android:tint:矢量圖的顏色,這個(gè)顏色值會(huì)覆蓋所有與color相關(guān)的屬性比如path的fillColor和strokeColor等;這個(gè)屬性會(huì)被API setColorFilter(ColorFilter)覆蓋。 -
android:tintMode:色彩混合模式,可選值有很多,下面詳細(xì)討論,默認(rèn)src_in。 -
android:autoMirrored:當(dāng)布局方向變成right-to-left的時(shí)候,矢量圖是否自動(dòng)鏡像,默認(rèn)false,這個(gè)屬性在API>=19才生效。
關(guān)于tintMode,先看下PorterDuff這類的源碼:
public class PorterDuff {
public PorterDuff() {
throw new RuntimeException("Stub!");
}
public static enum Mode {
CLEAR, /** [Sa, Sc] */
DST, /** [Da, Dc] */
DST_ATOP, /** [Sa, Sa * Dc + Sc * (1 - Da)] */
DST_IN, /** [Sa * Da, Sa * Dc] */
DST_OUT, /** [Da * (1 - Sa), Dc * (1 - Sa)] */
DST_OVER, /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
SRC, /** [Sa, Sc] */
SRC_ATOP, /** [Da, Sc * Da + (1 - Sa) * Dc] */
SRC_IN, /** [Sa * Da, Sc * Da] */
SRC_OUT, /** [Sa * (1 - Da), Sc * (1 - Da)] */
SRC_OVER, /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
DARKEN, /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
XOR, /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
LIGHTEN, /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
MULTIPLY, /** [Sa * Da, Sc * Dc] */
SCREEN, /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
/** Saturate(S + D) */
ADD,
OVERLAY;
private Mode() {
}
}
}
首先類名PorterDuff是什么意思呢?PorterDuff是兩個(gè)人名的組合: Thomas Porter和Tom Duff,他們1984年在ACM SIGGRAPH計(jì)算機(jī)圖形學(xué)發(fā)表論文《Compositing digital images》,最早提出圖形混合概念,極大地推動(dòng)了圖形圖像學(xué)的發(fā)展,有興趣的同學(xué)可以自行查閱資料。

PorterDuff共有18個(gè)模式可選,但是android:tintMode可選值只有六個(gè):MULTIPLY、SCREEN、ADD、SRC_ATOP、SRC_IN、SRC_OVER,(xml文件只提供這6個(gè)的原因我不知道,請(qǐng)大神留言告知)。當(dāng)然想使用其余12個(gè)tintMode模式也是可以的,需要用代碼調(diào)用API Drawable.setTintMode(PorterDuff.Mode)即可,可以達(dá)到相應(yīng)tintMode的效果。
tint屬性是Android 5.0引入的,Android 6.0又引入了drawableTint的屬性。
Button和TextView等一些組件會(huì)多出下面6個(gè)屬性:

ImageView會(huì)多出下面6個(gè)屬性:


我們對(duì)矢量圖進(jìn)行調(diào)色,先看效果如圖4所示,圖4中紅色文字表示xml允許使用的6個(gè)tintMode。下面貼出代碼:
// poker_a.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="400dp"
android:height="550dp"
android:tint="@android:color/holo_purple"
android:viewportHeight="550"
android:viewportWidth="400.0">
<group android:name="poker_diamond_a">
<path
android:name="border"
android:strokeWidth="7"
android:strokeColor="#96999c"
android:fillColor="@android:color/white"
android:pathData="M5 25a20 20 0 0 1 20 -20
h350a20 20 0 0 1 20 20v500a20 20 0 0 1 -20 20h-350a20 20 0 0 1 -20 -20v-500"/>
<path android:name="a"
android:strokeWidth="8"
android:strokeColor="@android:color/holo_red_dark"
android:strokeLineJoin="bevel"
android:pathData="M40 120
l40 -90
l40 90
l-16-35
h-48"/>
<path android:name="small_diamond" android:fillColor="@android:color/holo_blue_dark" android:pathData="M80 130l41 41l-41 41l-41 -41z"/>
<path android:name="big_diamond" android:fillColor="@android:color/holo_green_dark" android:pathData="M260 310l100 100l-100 100l-100 -100z"/>
</group>
</vector>
<dimen name="width">64dp</dimen>
<dimen name="height">88dp</dimen>
// activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="10dp"
android:tint="@android:color/transparent"/>
<View
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:layout_marginLeft="10dp"
android:background="@android:color/holo_purple"/>
<ImageView
android:id="@+id/CLEAR"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="10dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:id="@+id/DST"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a" />
<ImageView
android:id="@+id/DST_ATOP"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/DST_IN"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/DST_OUT"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/DST_OVER"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:id="@+id/SRC"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a" />
<ImageView
android:id="@+id/SRC_ATOP"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/SRC_IN"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/SRC_OUT"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/SRC_OVER"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:id="@+id/DARKEN"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a" />
<ImageView
android:id="@+id/XOR"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/LIGHTEN"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/MULTIPLY"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
<ImageView
android:id="@+id/SCREEN"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:id="@+id/ADD"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a" />
<ImageView
android:id="@+id/OVERLAY"
android:layout_width="@dimen/width"
android:layout_height="@dimen/height"
android:src="@drawable/poker_a"
android:layout_marginLeft="5dp" />
</LinearLayout>
</LinearLayout>
MainActivity.java代碼:
// MainActivity.java
public class MainActivity extends AppCompatActivity {
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((ImageView) findViewById(R.id.CLEAR)).getDrawable().mutate().setTintMode(PorterDuff.Mode.CLEAR);
((ImageView) findViewById(R.id.DST)).getDrawable().mutate().setTintMode(PorterDuff.Mode.DST);
((ImageView) findViewById(R.id.DST_ATOP)).getDrawable().mutate().setTintMode(PorterDuff.Mode.DST_ATOP);
((ImageView) findViewById(R.id.DST_IN)).getDrawable().mutate().setTintMode(PorterDuff.Mode.DST_IN);
((ImageView) findViewById(R.id.DST_OUT)).getDrawable().mutate().setTintMode(PorterDuff.Mode.DST_OUT);
((ImageView) findViewById(R.id.DST_OVER)).getDrawable().mutate().setTintMode(PorterDuff.Mode.DST_OVER);
((ImageView) findViewById(R.id.SRC)).getDrawable().mutate().setTintMode(PorterDuff.Mode.SRC);
((ImageView) findViewById(R.id.SRC_ATOP)).getDrawable().mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
((ImageView) findViewById(R.id.SRC_IN)).getDrawable().mutate().setTintMode(PorterDuff.Mode.SRC_IN);
((ImageView) findViewById(R.id.SRC_OUT)).getDrawable().mutate().setTintMode(PorterDuff.Mode.SRC_OUT);
((ImageView) findViewById(R.id.SRC_OVER)).getDrawable().mutate().setTintMode(PorterDuff.Mode.SRC_OVER);
((ImageView) findViewById(R.id.DARKEN)).getDrawable().mutate().setTintMode(PorterDuff.Mode.DARKEN);
((ImageView) findViewById(R.id.XOR)).getDrawable().mutate().setTintMode(PorterDuff.Mode.XOR);
((ImageView) findViewById(R.id.LIGHTEN)).getDrawable().mutate().setTintMode(PorterDuff.Mode.LIGHTEN);
((ImageView) findViewById(R.id.MULTIPLY)).getDrawable().mutate().setTintMode(PorterDuff.Mode.MULTIPLY);
((ImageView) findViewById(R.id.SCREEN)).getDrawable().mutate().setTintMode(PorterDuff.Mode.SCREEN);
((ImageView) findViewById(R.id.ADD)).getDrawable().mutate().setTintMode(PorterDuff.Mode.ADD);
((ImageView) findViewById(R.id.OVERLAY)).getDrawable().mutate().setTintMode(PorterDuff.Mode.OVERLAY);
}
}
色彩混合涉及到兩個(gè)對(duì)象,目標(biāo)對(duì)象和源對(duì)象,這里的目標(biāo)對(duì)象是poker_a.xml這個(gè)矢量圖,源對(duì)象是tint設(shè)置的顏色值,為啥這樣說呢,記著這個(gè)原則,先繪制的是目標(biāo)對(duì)象,我們就是要對(duì)目標(biāo)對(duì)象進(jìn)行調(diào)色。對(duì)目標(biāo)對(duì)象調(diào)色后的對(duì)象叫做復(fù)合對(duì)象。
有了這兩個(gè)對(duì)象,怎么進(jìn)行調(diào)色呢?這里有很多計(jì)算公式,公式中又有很多元素,先來看下這些元素:
* Sa:Source alpha,源對(duì)象的Alpha通道;
* Sc:Source color,源對(duì)象的顏色;
* Da:Destination alpha,目標(biāo)對(duì)象的Alpha通道;
* Dc:Destination color,目標(biāo)對(duì)象的顏色;
* [a,c]:對(duì)象的ARGB值,a表示alpha,c表示color。
下面對(duì)這18個(gè)tintMode進(jìn)行剖析,該文受到這篇文章的啟發(fā)。
CLEAR
復(fù)合對(duì)象的ARGB值是[0,0],完全透明,相當(dāng)于清除畫布上的圖像了。
DST
[Da, Dc],只保留目標(biāo)對(duì)象的alpha和color值,因此繪制出來的只有目標(biāo)對(duì)象,相當(dāng)于根本就沒有進(jìn)行調(diào)色。
DST_ATOP
[Sa, Sa * Dc + Sc * (1 - Da)],兩者相交處繪制目標(biāo)對(duì)象,不相交的地方繪制源對(duì)象,并且相交處的效果會(huì)受到源對(duì)象和目標(biāo)對(duì)象alpha的影響。
DST_IN
[Sa * Da, Sa * Dc],兩者相交的地方繪制目標(biāo)對(duì)象,不相交的地方不進(jìn)行繪制,并且相交處的效果會(huì)受到源對(duì)象對(duì)應(yīng)地方透明度的影響。
DST_OUT
[Da * (1 - Sa), Dc * (1 - Sa)],兩者不相交的地方繪制目標(biāo)對(duì)象,相交處根據(jù)源對(duì)象alpha進(jìn)行過濾,完全不透明處則完全過濾,完全透明則不過濾。(親測(cè)正確)
DST_OVER
[Sa + (1 - Sa)Da, Rc = Dc + (1 - Da)Sc],目標(biāo)對(duì)象繪制在源對(duì)象的上方。
SRC
[Sa, Sc],只保留源對(duì)象的alpha和color,因此繪制出來只有源對(duì)象。
SRC_ATOP
[Da, Sc * Da + (1 - Sa) * Dc],兩者相交處繪制源對(duì)象,不相交的地方繪制目標(biāo)對(duì)象,并且相交處的效果會(huì)受到源對(duì)象和目標(biāo)對(duì)象alpha的影響。
SRC_IN
[Sa * Da, Sc * Da],兩者相交處繪制源對(duì)象,不相交的地方不進(jìn)行繪制,并且相交處的效果會(huì)受到源對(duì)象對(duì)應(yīng)地方透明度的影響。
SRC_OUT
[Sa * (1 - Da), Sc * (1 - Da)],兩者不相交的地方繪制源對(duì)象,相交處根據(jù)目標(biāo)對(duì)象alpha進(jìn)行過濾,完全不透明處則完全過濾,完全透明則不過濾。(親測(cè)正確)
SRC_OVER
[Sa + (1 - Sa)Da, Rc = Sc + (1 - Sa)Dc],源對(duì)象繪制在目標(biāo)對(duì)象的上方。
DARKEN
[Sa + Da - SaDa, Sc(1 - Da) + Dc(1 - Sa) + min(Sc, Dc)],顧名思義,效果會(huì)變暗。進(jìn)行對(duì)應(yīng)像素比較,取較暗值(即較小值),如果色值相同則進(jìn)行混合;如果兩個(gè)對(duì)象都完全不透明,取較暗值,否則使用上面算法進(jìn)行計(jì)算,受到源對(duì)象和目標(biāo)對(duì)象對(duì)應(yīng)色值和alpha值影響。結(jié)果復(fù)合對(duì)象的alpha值會(huì)變大。
XOR
[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc],不相交的地方按原樣繪制源對(duì)象和目標(biāo)對(duì)象,相交的地方受到對(duì)應(yīng)alpha和顏色值影響,按公式進(jìn)行計(jì)算,如果都完全不透明則相交處完全不繪制。
LIGHTEN
[Sa + Da - SaDa, Sc(1 - Da) + Dc(1 - Sa) + max(Sc, Dc)],顧名思義,效果會(huì)變亮。進(jìn)行對(duì)應(yīng)像素比較,取較亮值(即較大值),如果色值相同則進(jìn)行混合;如果兩個(gè)對(duì)象都完全不透明,取較亮值,否則使用上面算法進(jìn)行計(jì)算,受到源對(duì)象和目標(biāo)對(duì)象對(duì)應(yīng)色值和alpha值影響。
MULTIPLY
[Sa * Da, Sc * Dc],正片疊底,即查看每個(gè)通道中的顏色信息,目標(biāo)色與源色復(fù)合。結(jié)果色總是較暗的顏色。任何顏色與黑色復(fù)合產(chǎn)生黑色,任何顏色與白色復(fù)合保持不變。(這個(gè)理論貌似和現(xiàn)實(shí)生活的顏色混合的結(jié)果不一致,現(xiàn)實(shí)生活中黃色和白色混合會(huì)是黃白色而不是白色)。
SCREEN
[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc],濾色模式,這個(gè)模式與我們所用的顯示屏原理相同,因此也被翻譯成屏幕模式;保留兩個(gè)圖層中較白的部分,較暗的部分被遮蓋,圖層中純黑的部分變成完全透明,純白部分完全不透明,其他的顏色根據(jù)顏色級(jí)別產(chǎn)生半透明的效果。
ADD
Saturate(S + D),飽和度疊加
OVERLAY
算法同時(shí)進(jìn)行進(jìn)行 Multiply(正片疊底)混合還是 Screen(屏幕)混合,是進(jìn)行 Multiply混合還是 Screen混合,取決于目標(biāo)對(duì)象的顏色值,目標(biāo)對(duì)象顏色的高光與陰影部分的亮度等細(xì)節(jié)會(huì)被保留。
這18個(gè)模式終于介紹完了,再次感謝這篇文章的作者。
path屬性
- android:strokeLineCap:顧名思義,設(shè)置線條的帽子,round圓角、square正方形、butt臀,默認(rèn)是butt;
- android:strokeLineJoin:線條拐彎處的樣式,round圓角、bevel斜角、miter斜切尖角,默認(rèn)是miter;
- android:strokeMiterLimit:android:strokeLineJoin為miter的時(shí)候這個(gè)屬性才發(fā)揮作用。設(shè)置miter斜切尖角長(zhǎng)度(用miter_length表示)與線條寬度(用line_width表示)比例值的上限,默認(rèn)是4,strokeMiterLimit = miter_length / line_width,這個(gè)屬性設(shè)定了這個(gè)比例的最大值,超過這個(gè)值的尖角不再顯示尖角而是bevel斜角。圖5圖6
如果你希望尖角多一些,就把這個(gè)屬性設(shè)置大一些。在特別尖的拐彎處的點(diǎn),點(diǎn)的這個(gè)比例可能大與strokeMiterLimit,那么就不顯示尖角效果而是類似bevel斜角的效果,這樣看起來不是很突兀,比較美觀。
圖5和圖6來自文章; - android:trimPathStart:從路徑起始位置截?cái)嗦窂降谋嚷?,取值范?-1,默認(rèn)0;
- android:trimPathEnd:從路徑結(jié)束位置截?cái)嗦窂降谋嚷?,取值范?-1,默認(rèn)1;
- android:trimPathOffset:設(shè)置路徑截取的偏移比例,取值范圍0-1,默認(rèn)0;
利用android:trimPathStart和android:trimPathEnd可以做一些入場(chǎng)和出場(chǎng)動(dòng)畫,鏈接 - android:fillType:API 24才引入的這個(gè)屬性,取值nonZero和evenOdd,默認(rèn)nonZero。
關(guān)于android:fillType這個(gè)屬性,需要花點(diǎn)篇幅討論下。討論這個(gè)屬性之前,先看下代碼及其對(duì)應(yīng)的效果:
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="600"
android:viewportWidth="600">
<path
android:name="noneZero"
android:strokeWidth="2"
android:strokeColor="#B32D20"
android:fillColor="#3C8FC1"
android:pathData="M20 120 a100 100 0 1 1 200 0 a100 100 0 1 1 -200 0
M40 120 a80 80 0 1 1 160 0 a80 80 0 1 1 -160 0"/>
<path
android:name="evenOdd"
android:strokeWidth="2"
android:strokeColor="#B32D20"
android:fillColor="#3C8FC1"
android:fillType="evenOdd"
android:pathData="M260 120 a100 100 0 1 1 200 0 a100 100 0 1 1 -200 0
M280 120 a80 80 0 1 1 160 0 a80 80 0 1 1 -160 0"/>
</vector>

代碼中有兩條path,前者的fillType是默認(rèn)的noneZero,后者的fillType是evenOdd,除了這兩個(gè)屬性外,其余屬性一模一樣(name屬性和M指令的起始位置為了顯示的區(qū)別,忽略好吧??)。兩者的效果圖如圖7所示。fillType的原理是什么呢?為啥會(huì)導(dǎo)致這樣的效果呢?
這里需要提一點(diǎn),代碼中每一條path都繪制了兩個(gè)圓,四個(gè)圓的每一個(gè)圓都是通過兩條a指令繪制完成的,如果你不清楚a指令的參數(shù),請(qǐng)仔細(xì)看完Android矢量圖I--VectorDrawable基礎(chǔ)這篇文章并搞明白a指令后再回來看本文。這8條a指令的第5個(gè)參數(shù)都是1表示順時(shí)針,請(qǐng)記住都是順時(shí)針,因?yàn)閒illType屬性值noneZero跟path的方向有很大關(guān)系。
首先來看下默認(rèn)的noneZero值。多看維基百科,那里的解釋最權(quán)威。有一個(gè)多邊形C,我們判斷是否要對(duì)C的子多邊形C1形成的一塊區(qū)域進(jìn)行填充,那么先定義一個(gè)變量value=0,根據(jù)value的值來決定是否對(duì)C1進(jìn)行填充;在這個(gè)C1區(qū)域內(nèi)任意選擇一個(gè)點(diǎn)P,從這個(gè)點(diǎn)P向任意方向發(fā)射一條無限長(zhǎng)的直線L,但是這個(gè)方向需要直線L與C1至少有一個(gè)交點(diǎn),然后找出直線L與C所有的交點(diǎn),每個(gè)交點(diǎn)處的方向是順時(shí)針value++,順時(shí)針value--,如果最后的value不是0,那么就對(duì)C1填充,否則不填充。
如果你不明白上面粗體文字的含義,那就根據(jù)上面代碼名字為noneZero這個(gè)path來分析下。這條path包含4個(gè)a指令,其實(shí)也就是4個(gè)形狀為弧線的子path:弧線AB、弧線BA、弧線CD、弧線DC,相應(yīng)地我們就要判斷四塊區(qū)域(outer_a、outer_b、inner_a、inner_b)是否要進(jìn)行fill(填充,這里的填充默認(rèn)是填充內(nèi)部)。對(duì)于區(qū)域outer_a,我們?cè)谄鋬?nèi)部選擇任意一點(diǎn)P,從P發(fā)射一條無限長(zhǎng)直線,直線需要與子多邊形弧線AB相交至少一個(gè)點(diǎn),該點(diǎn)是P1,直線與多邊形C的交點(diǎn)是P1,是順時(shí)針,value=P1=1,不為0,該區(qū)域填充;對(duì)于區(qū)域inner_a,我們?cè)谄鋬?nèi)部選擇任意一點(diǎn)R,從R發(fā)射一條無限長(zhǎng)直線,直線需要與子多邊形弧線CD相交至少一個(gè)點(diǎn),該點(diǎn)是R1,直線與多邊形C的交點(diǎn)是R1和R2,value=R1+R1=1+1=2,不為0,該區(qū)域填充。如圖8所示,

為了加深下印象,再來舉個(gè)例子??聪旅娴拇a:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="600"
android:viewportWidth="600">
<path
android:strokeWidth="2"
android:strokeColor="#B32D20"
android:fillColor="#3C8FC1"
android:pathData="M20 320 a100 100 0 1 0 200 0
M40 320 a80 80 0 1 1 160 0 a80 80 0 1 1 -160 0"/>
</vector>

你運(yùn)行上面的代碼,不出問題的矢量圖應(yīng)該如圖9所示。代碼首先從A到B逆時(shí)針繪制橢圓弧,然后從C到D順時(shí)針繪制橢圓弧,最后從D到C順時(shí)針繪制橢圓弧。最終的多邊形C包含三個(gè)子區(qū)域outer_a、inner_a、inner_b。對(duì)于outer_a,其value=P=-1,非0那就填充;對(duì)于inner_a,其value=Q1=1,非0那就填充;對(duì)于inner_b,其value=R1+R2=1+(-1)=0,0那就不填充。
如果你已經(jīng)明白了noneZero,那么evenOdd就非常非常簡(jiǎn)單了,先看維基百科,它與指針方向沒有關(guān)系了,一句話你就能明白:如果虛擬直線與多邊形C的交點(diǎn)數(shù)目為奇數(shù)就填充內(nèi)部,偶數(shù)就不填充內(nèi)部。如果還不明白,就舉個(gè)例子,看代碼:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="600"
android:viewportWidth="600">
<path
android:strokeWidth="2"
android:strokeColor="#B32D20"
android:fillColor="#3C8FC1"
android:fillType="evenOdd"
android:pathData="M20 320 a100 100 0 1 0 200 0
M40 320 a80 80 0 1 1 160 0 a80 80 0 1 1 -160 0"/>
</vector>
上面代碼的結(jié)果也是如圖9所示,那么拿圖9來解釋:對(duì)于outer_a,其只有一個(gè)交點(diǎn)P,奇數(shù)那就填充;對(duì)于inner_a,其只有一個(gè)交點(diǎn)Q,奇數(shù)那就填充;對(duì)于inner_b,有兩個(gè)交點(diǎn)R1和R2,偶數(shù)那就不填充。
懷著緊張的心情,fillType屬性終于介紹了完了,真害怕寫錯(cuò)而誤導(dǎo)讀者,如果有錯(cuò)誤的地方或者有更好的分析方法,請(qǐng)大神批評(píng)指正。這里有兩篇文章,幫助理解fillType:文章1、文章2。再說一點(diǎn),如果你是用tint屬性對(duì)矢量圖進(jìn)行調(diào)色,注意fillType屬性值會(huì)對(duì)結(jié)果造成同樣的影響。
最后,你如果查看Paint類的內(nèi)部類FillType的源碼,會(huì)發(fā)現(xiàn)還有另外兩種fillType:
/**
* Enum for the ways a path may be filled.
*/
public enum FillType {
// these must match the values in SkPath.h
/**
* Specifies that "inside" is computed by a non-zero sum of signed
* edge crossings.
*/
WINDING (0),
/**
* Specifies that "inside" is computed by an odd number of edge
* crossings.
*/
EVEN_ODD (1),
/**
* Same as {@link #WINDING}, but draws outside of the path, rather than inside.
*/
INVERSE_WINDING (2),
/**
* Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside.
*/
INVERSE_EVEN_ODD(3);
FillType(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
對(duì)這另外兩種fillType,Google程序員的注釋已經(jīng)解釋的很清楚了,我就不解釋了,否則可能越解釋越亂,越亂越糊涂。
這篇文章和上篇文章Android矢量圖I--VectorDrawable基礎(chǔ)一起把VectorDrawable的全部屬性都介紹了,相信使用矢量圖應(yīng)該更加輕松了,??。
后續(xù)文章會(huì)對(duì)矢量動(dòng)畫AnimatedVectorDrawable進(jìn)行研究,敬請(qǐng)期待。

