前言:最近在学性能优化时接触到Tint这个非常有意思的属性,接下来用RadioButton来实现。
Tint(着色器)它能够实现图片变色,利用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不管是哪种选中状态必须是同一张图片,否则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需要代码动态设置
/**
* 为了兼容API<23 drawableTint不起作用
* 此代码是在MVP架构项目中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()来获取而不是直接获取呢?接下来看看源码是怎么实现的。
先来看看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.
* 为当前平台选择正确的实现
*/
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对象是根据不同平台创建的,IMPL.wrap(drawable)方法又写了什么呢,接下来点进去看看。其中DrawableCompatApi23Impl里的wrap方法直接返回drawable并没有做封装处理。
public Drawable wrap(Drawable drawable) {
if (!(drawable instanceof TintAwareDrawable)) {
return new DrawableWrapperApi14(drawable);
}
return drawable;
}
这里判断传进来的drawable是否为TintAwareDrawable类型,是就直接返回当前drawable。TintAwareDrawable既然是tint开头的,那就应该和tint相关的一个类。看下TintAwareDrawable里面有什么。
/**
* 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接口提供了三个回调,上面判断不是TintAwareDrawable类型就创建DrawableWrapperApi14对象,那么DrawableWrapperApi14应该实现这个接口。
/**
* 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这个对象将传进来的drawable封装一遍,具体是怎么封装的就不带着跟源码了,感兴趣的请自行查看源码。
2.ColorStateList是颜色状态列表,设置tint要用到,可以通过构造方法创建,也可以从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();
}
这个构造方法目的是创建一个状态跟颜色一一映射的列表