Tint 是什么?
Tint,翻译过来意思就是染色。自Android-L(5.0)之后,出现了新的设计语言:Material design,瞬间丰富了app的设计主题与动画,同时也对开发者对于用户交互提出了指导。那主题之间的切换是如何做到的呢?使用Theme.AppCompat,随着Theme中的colorPrimary,colorPrimaryDark,colorAccent颜色的变化,相应主题就发生改变,其实这一切都是Tint完成的。其实严格来说,在5.0之前该功能我们就能实现,只是google在5.0后对它关键字化,将它从PorterDuffColorFilter独立出来,在xml和代码中均可以使用tint的一系列指令和api实现相关功能。在后面我们会就这一问题进行讨论。
好处?节约资源啊,同1张图片的资源,如果用的合适,可以使用在多个地方。
Tint 小实践.
项目中现在有这样一个需求。消耗占比星级显示规则
消耗占比 | 星级 | 颜色 |
<7.5% | 一星 | 红色 |
[ 7.5%,10%) | 二星 | 橙色 |
≥10% | 三星 | 绿色 |
实际就是根据消耗占比,显示不同的星星个数以及颜色。包括背景色星星在内,设计给我们切了4张图。实际上如果使用tint功能,我们是不用添加这几张图片的。
假设你的项目对版本的支持比较高(>=21),那么下列几行代码就基本可以实现相关功能。
<RatingBar
android:id="@+id/rating_1"
android:layout_width="wrap_content"
android:progressTint="@color/data_text_star1"
android:layout_height="wrap_content" />
代码中,我们获取到RatingBar,然后在代码重新设置颜色即可。
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RatingBar myRatingBar1 = (RatingBar) findViewById(R.id.rating_1);
但是大部分时候,我们是不会支持这么高版本,我们需要兼容以前的版本,这个时候如何做到呢。不得不说,这样的功能对于客户端开发者来说,的确是不错福音,有时候能够节省一定量的图片资源,在ios7中也加入了tintColor这一属性。那它是如何实现的呢。关键代码其实也就几行:
if(filter == null){
// Caches misses create a color filter and add it to the cache
filter = new ProterDuffColorFilter(color, mode);
}
d.setColorFilter(filter);
没错,就是通过ProterDuffColorFilter实现的.这类东东我们在使用自定义圆角图片的时候,经常跟它打交道,这一次google直接在sdk中帮我们实现了这个功能,获取到图片的drawable,然后设置drawable的colorFilter,默认的mode是src_in的,典型的新瓶装旧酒。
如果使用以前的api,我们应该如何实现呢:
LayerDrawable drawable = (LayerDrawable) getProgressDrawable();
Drawable drawable0 = drawable.getDrawable(2);
tintDrawable(drawable0,ColorStateList.valueOf(getResources().getColor(R.color.data_text_star2)));
Drawable drawable1 = drawable.getDrawable(1);
drawable0.setColorFilter(getResources().getColor(R.color.data_text_star2), PorterDuff.Mode.SRC_IN);
drawable1.setColorFilter(getResources().getColor(R.color.data_text_star2), PorterDuff.Mode.SRC_IN);
会稍微麻烦一些,我们看看不妨看看系统代码也是如何实现的:
@RemotableViewMethod
public void setProgressTintList(@Nullable ColorStateList tint) {
if (mProgressTintInfo == null) {
mProgressTintInfo = new ProgressTintInfo();
}
mProgressTintInfo.mProgressTintList = tint;
mProgressTintInfo.mHasProgressTint = true;
if (mProgressDrawable != null) {
applyPrimaryProgressTint();
}
}
继续看,核心方法是applyPrimaryProgressTint():
private void applyPrimaryProgressTint() {
if (mProgressTintInfo.mHasProgressTint
|| mProgressTintInfo.mHasProgressTintMode) {
final Drawable target = getTintTarget(R.id.progress, true);
if (target != null) {
if (mProgressTintInfo.mHasProgressTint) {
target.setTintList(mProgressTintInfo.mProgressTintList);
}
if (mProgressTintInfo.mHasProgressTintMode) {
target.setTintMode(mProgressTintInfo.mProgressTintMode);
}
// The drawable (or one of its children) may not have been
// stateful before applying the tint, so let's try again.
if (target.isStateful()) {
target.setState(getDrawableState());
}
}
}
}
这次换成了drawable的setTinitList():
@Override
public void setTintList(ColorStateList tint) {
final BitmapState state = mBitmapState;
if (state.mTint != tint) {
state.mTint = tint;
mTintFilter = updateTintFilter(mTintFilter, tint, mBitmapState.mTintMode);
invalidateSelf();
}
}
updateTintFilter():
@Nullable PorterDuffColorFilter updateTintFilter(@Nullable PorterDuffColorFilter tintFilter,
@Nullable ColorStateList tint, @Nullable PorterDuff.Mode tintMode) {
if (tint == null || tintMode == null) {
return null;
}
final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
if (tintFilter == null) {
return new PorterDuffColorFilter(color, tintMode);
}
tintFilter.setColor(color);
tintFilter.setMode(tintMode);
return tintFilter;
}
找到了,的的确确系统调来调去,最后就是通过PorterDuffColorFilter来实现的。
上面只是单单举例ratingbar中的使用,实际上Tint适用于所有包含图片的控制。这样一个例子:
下载地址: demo下载