學(xué)習(xí)tint的目的:
1.一張矢量圖適配所有顏色(媽媽再也不要擔(dān)心我找圖了)。
2.更優(yōu)雅的selector實(shí)現(xiàn)方式。
[圖片上傳失敗...(image-3878ff-1510058207692)]](http://upload-images.jianshu.io/upload_images/1757399-c52e20090d67b2e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
小試牛刀,一張矢量圖適配所有顏色。
如何在代碼中實(shí)現(xiàn)下圖效果
方法一:xml
方法很簡(jiǎn)單直接看代碼
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image"
android:src="@mipmap/icon"
android:clickable="true"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image2"
android:src="@mipmap/icon"
android:tint="#FFCDD2"
android:clickable="true"
/>
用到的屬性android:tint="@color"
至于原理不做過(guò)多說(shuō)明,有興趣看看源碼比較簡(jiǎn)單,也可以參考一下官網(wǎng)。
方法二:代碼實(shí)現(xiàn)
Drawable drawable = ContextCompat.getDrawable(this,R.mipmap.icon);
Drawable.ConstantState state = drawable.getConstantState();
Drawable drawable1 = DrawableCompat.wrap(state == null ? drawable : state.newDrawable()).mutate();
drawable1.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
DrawableCompat.setTint(drawable,ContextCompat.getColor(this,R.color.pink));
imageView.setImageDrawable(drawable);
imageView1.setImageDrawable(drawable1);
DrawableCompat類:是Drawable的向下兼容類,我們?yōu)榱嗽?.0一下兼容tint屬性而使用的,有興趣的看看源碼哦,也是很簡(jiǎn)單的一個(gè)兼容類。
wrap方法:使用tint就必須調(diào)用該方法對(duì)Drawable進(jìn)行一次包裝。
mutate方法:(個(gè)人簡(jiǎn)單的理解就是類似于對(duì)象的深拷貝與淺拷貝),如果不調(diào)用該方法,我們進(jìn)行操作的就是原drawable,著色之后原drawable也改變的,所有兩個(gè)ImageView都會(huì)顯示著色之后的drawable。調(diào)用mutate后會(huì)對(duì)ConstantState進(jìn)行一次拷貝,詳情可看源碼,以及參考。
恩,目的一基本完成了,我們來(lái)看目的二。
更加優(yōu)雅的使用selector
第一次嘗試
為了更加優(yōu)雅的使用selector可謂是踩了很多坑啊,但是實(shí)踐才是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),遇到坑就多去看看源碼。
以前使用selector是這樣的。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@mipmap/icon_pressed"></item>
<item android:drawable="@mipmap/icon_normal"></item>
</selector>
所以咯,我們需要在mipmap中放置兩張圖片,但是目的一中我們知道一張矢量圖是能適配出所有顏色的。所以我們開(kāi)始踩坑。
我們可以在color中定義一個(gè)selector,然后設(shè)置tint屬性。
//color/icon.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@color/pink" ></item>
<item android:color="@color/pink1"></item>
</selector>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image"
android:src="@mipmap/icon"
android:tint="@color/icon"
android:clickable="true"
/>
設(shè)置后你會(huì)發(fā)現(xiàn),并沒(méi)有效果??!這是為什么呢,我們看看BitmapDrawable源碼吧。
@Override
protected boolean onStateChange(int[] stateSet) {
final BitmapState state = mBitmapState;
if (state.mTint != null && state.mTintMode != null) {
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
return true;
}
return false;
}
DEBUG發(fā)現(xiàn)updateTintFilter()方法已經(jīng)執(zhí)行,為什么沒(méi)有效果呢?
進(jìn)一步DEBUG發(fā)現(xiàn)界面未刷新,invalidateSelf()方法未調(diào)用,ImageView的onDraw()方法未執(zhí)行,所以Drawable的draw()方法也未執(zhí)行。所以暫時(shí)這種方法是行不通的。(但是在java邏輯代碼中設(shè)置了在6.0以下是可以實(shí)現(xiàn)的,具體為什么我也有點(diǎn)納悶…可以去看看我的demo)
第二次嘗試
于是我們使用StateListDrawable,那么先在xml中試試可行性吧。
//drawable/icon.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@drawable/icon" ></item>
<item android:color="@color/pink1"></item>
</selector>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image"
android:src="@drawable/icon"
android:tint="@color/icon"
android:clickable="true"
/>
我們?cè)赿rawable目錄下icon.xml中兩種狀態(tài)都設(shè)置的同一張圖。(為什么要設(shè)置同一張圖是有根據(jù)的,因?yàn)镈rawableStateList源碼中,如果你當(dāng)然狀態(tài)未單獨(dú)設(shè)置drawable,將不會(huì)觸發(fā)刷新,具體看源碼,本篇側(cè)重于實(shí)踐。)
恩,到這里你就會(huì)驚人的發(fā)現(xiàn),效果出來(lái)了!(點(diǎn)擊后變色)
但是很遺憾6.0以下會(huì)直接crash的,因?yàn)椴患嫒?,所以我們必須在java代碼中設(shè)置。按照這個(gè)思路擼出代碼。
Drawable drawable = ContextCompat.getDrawable(this,R.mipmap.icon);
int[] colors = new int[] { ContextCompat.getColor(this,R.color.pink),ContextCompat.getColor(this,R.color.pink1)};
int[][] states = new int[2][];
states[0] = new int[] { android.R.attr.state_pressed};
states[1] = new int[] {};
ColorStateList colorList = new ColorStateList(states, colors);
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(states[0],drawable);//注意順序
stateListDrawable.addState(states[1],drawable);
Drawable.ConstantState state = stateListDrawable.getConstantState();
drawable = DrawableCompat.wrap(state == null ? stateListDrawable : state.newDrawable()).mutate();
DrawableCompat.setTintList(drawable,colorList);
imageView.setImageDrawable(drawable);
(代碼有點(diǎn)多,但是這樣做是很值得的。其實(shí)這樣做感覺(jué)更符合邏輯一點(diǎn),不過(guò)期間踩了很多坑,特別是在做6.0和6.0以下適配的時(shí)候)
恩,效果出來(lái)了,具體原因可以去看看StateListDrawable源碼,然后自己DEBUG一下。
到這里踩坑完成了。更優(yōu)雅的實(shí)現(xiàn)selector,既減少了apk大小又節(jié)約了內(nèi)存。