RadioButton用Tint屬性實現(xiàn)一張圖片2中效果

前言:最近在學(xué)性能優(yōu)化時接觸到Tint這個非常有意思的屬性,接下來用RadioButton來實現(xiàn)。

Tint(著色器)它能夠?qū)崿F(xiàn)圖片變色,利用Tint可以將一張圖片著色成不同顏色的圖片,原本需要多張相同圖片不同顏色則可以用一張圖片替代,能夠減少apk體積。

效果如圖:

01.gif

接下來直接貼代碼

 <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類

  1. 首先要獲取想要著色的圖片。第一時間想到的是用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)跟顏色一一映射的列表

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容