前言:最近在學(xué)性能優(yōu)化時接觸到Tint這個非常有意思的屬性,接下來用RadioButton來實現(xiàn)。
Tint(著色器)它能夠?qū)崿F(xiàn)圖片變色,利用Tint可以將一張圖片著色成不同顏色的圖片,原本需要多張相同圖片不同顏色則可以用一張圖片替代,能夠減少apk體積。
效果如圖:

接下來直接貼代碼
<RadioButton
android:id="@+id/rb_video"
style="@style/rbStyle"
android:checked="true"
android:drawableTop="@drawable/tint_rb_video_src_selector"
android:text="@string/file_video"/>
<style name="rbStyle">//公共樣式
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:gravity">center</item>
<item name="android:drawablePadding">3dp</item>
<item name="android:layout_weight">1</item>
<item name="android:textSize">12sp</item>
<item name="android:textColor">@color/tint_rb_video_texcolor_selector</item>
<item name="android:button">@android:color/transparent</item>
<item name="android:drawableTint">@color/tint_rb_video_color_selector</item>
</style>
文字直接用選擇器好了
<item name="android:textColor">@color/tint_rb_video_texcolor_selector</item>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/rbSelectColor"android:state_checked="true">
<item android:color="@color/rbDefaultColor"/>
</selector>
對于RadioButton里的drawableTint屬性,需要API>=23才能生效
API>=23可以直接在xml布局里main寫,要注意的是:選擇器里的@drawable不管是哪種選中狀態(tài)必須是同一張圖片,否則drawableTint不起作用
<item name="android:drawableTint">@color/tint_rb_video_color_selector</item>
android:drawableTop="@drawable/tint_rb_video_src_selector"
tint_rb_video_src_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_file_video" android:state_checked="true"/>
<item android:drawable="@drawable/ic_file_video"/>
</selector>
兼容API<23需要代碼動態(tài)設(shè)置
/**
* 為了兼容API<23 drawableTint不起作用
* 此代碼是在MVP架構(gòu)項目中Presenter copy的,所以要傳一個context
*
* @param drawableId 需要更改的圖片ID
* @return 返回修改后的圖片
*/
@Override
public Drawable setDrawableTopTintColor(int drawableId, Context context) {
Drawable drawable = DrawableCompat.wrap(context.getResources().getDrawable(drawableId));
ColorStateList colorStateList = context.getResources().getColorStateList(R.color.tint_rb_video_color_selector);
DrawableCompat.setTintList(drawable, colorStateList);
return drawable;
}
從源碼淺析Tint
既然要兼容低版本的API,那么就要用v4包下的DrawableCompat類
- 首先要獲取想要著色的圖片。第一時間想到的是用DrawableCompat.wrap()方法來獲取圖片,看到這里有人可能會想為什么要用wrap()來獲取而不是直接獲取呢?接下來看看源碼是怎么實現(xiàn)的。
先來看看DrawableCompat.wrap()方法寫了什么。
public static Drawable wrap(@NonNull Drawable drawable) {
return IMPL.wrap(drawable);
}
可以看到直接返回IMPL.wrap(drawable),但這個IMPL又是什么呢?
/**
* Select the correct implementation to use for the current platform.
* 為當(dāng)前平臺選擇正確的實現(xiàn)
*/
static final DrawableCompatBaseImpl IMPL;
static {
if (Build.VERSION.SDK_INT >= 23) {
IMPL = new DrawableCompatApi23Impl();
} else if (Build.VERSION.SDK_INT >= 21) {
IMPL = new DrawableCompatApi21Impl();
} else if (Build.VERSION.SDK_INT >= 19) {
IMPL = new DrawableCompatApi19Impl();
} else if (Build.VERSION.SDK_INT >= 17) {
IMPL = new DrawableCompatApi17Impl();
} else {
IMPL = new DrawableCompatBaseImpl();
}
}
可以看到這個IMPL對象是根據(jù)不同平臺創(chuàng)建的,IMPL.wrap(drawable)方法又寫了什么呢,接下來點進(jìn)去看看。其中DrawableCompatApi23Impl里的wrap方法直接返回drawable并沒有做封裝處理。
public Drawable wrap(Drawable drawable) {
if (!(drawable instanceof TintAwareDrawable)) {
return new DrawableWrapperApi14(drawable);
}
return drawable;
}
這里判斷傳進(jìn)來的drawable是否為TintAwareDrawable類型,是就直接返回當(dāng)前drawable。TintAwareDrawable既然是tint開頭的,那就應(yīng)該和tint相關(guān)的一個類??聪耇intAwareDrawable里面有什么。
/**
* Interface which allows a {@link android.graphics.drawable.Drawable} to receive tinting calls
* from {@code DrawableCompat}.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
public interface TintAwareDrawable {
void setTint(@ColorInt int tint);
void setTintList(ColorStateList tint);
void setTintMode(PorterDuff.Mode tintMode);
}
TintAwareDrawable接口提供了三個回調(diào),上面判斷不是TintAwareDrawable類型就創(chuàng)建DrawableWrapperApi14對象,那么DrawableWrapperApi14應(yīng)該實現(xiàn)這個接口。
/**
* Creates a new wrapper around the specified drawable.
*
* @param dr the drawable to wrap
*/
DrawableWrapperApi14(@Nullable Drawable dr) {
mState = mutateConstantState();
// Now set the drawable...
setWrappedDrawable(dr);
}
DrawableWrapperApi14這個對象將傳進(jìn)來的drawable封裝一遍,具體是怎么封裝的就不帶著跟源碼了,感興趣的請自行查看源碼。
2.ColorStateList是顏色狀態(tài)列表,設(shè)置tint要用到,可以通過構(gòu)造方法創(chuàng)建,也可以從xml獲取
/**
* Creates a ColorStateList that returns the specified mapping from states to colors.
*/
public ColorStateList(int[][] states, @ColorInt int[] colors) {
mStateSpecs = states;
mColors = colors;
onColorsChanged();
}
這個構(gòu)造方法目的是創(chuàng)建一個狀態(tài)跟顏色一一映射的列表