简单描述使用细节场景,根据界面颜色值动态切换shape的背景色
这里想到了tint 但因为是动态修改,在xml中使用更方便
查看tint的源码:
@Override
public void setTintList(ColorStateList tint) {
final BitmapState state = mBitmapState;
if (state.mTint != tint) {
state.mTint = tint;
mTintFilter = updateTintFilter(mTintFilter, tint, mBitmapState.mTintMode);
// 绘制 最终渲染改变颜色
invalidateSelf();
}
}
Drawable 的子类重写android.graphics.drawable.Drawable#setTintList 方法 调用:updateTintFilter
/**
* Ensures the tint filter is consistent with the current tint color and
* mode.
*/
@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);
}
// 注意这里PorterDuffColorFilter 的 setColor 和 setMode 和 后面要提到的setColorFilter 一个意思【也即兼容替换方案】
tintFilter.setColor(color);
tintFilter.setMode(tintMode);
return tintFilter;
}
or v4包下的
private boolean updateTint(int[] state) {
if (this.mTintList != null && this.mTintMode != null) {
int color = this.mTintList.getColorForState(state, this.mTintList.getDefaultColor());
Mode mode = this.mTintMode;
if (!this.mColorFilterSet || color != this.mCurrentColor || mode != this.mCurrentMode) {
//关注 这里的 setColorFilter
this.setColorFilter(color, mode);
this.mCurrentColor = color;
this.mCurrentMode = mode;
this.mColorFilterSet = true;
return true;
}
} else {
this.mColorFilterSet = false;
this.clearColorFilter();
}
return false;
}
**PorterDuffColorFilter 是ColorFilter的子类
从上面的源码可以得出最终都通过了ColorFilter完成了color的改变,不管是xml还是动态代码都是基于这个,所以可以直接使用 如下:
//but 需要care 兼容性 target 21
@TargetApi(21)
private void tintColor() {
Drawable drawable = getResources().getDrawable(R.drawable.selector_btn_bg);
int color = getResources().getColor(R.color.colorAccent);
drawable.setTint(color);
}
如果非要使用tint 来改变color,可通过兼容性方案setColorFilter 或者如下:
通过v4包下的DrawableCompat类完成兼容tint能力【源码即前面已经提到的源码中的 or 部分】
Drawable wrapDrawable = DrawableCompat.wrap(drawable);
wrapDrawable.setTint(color);
综上总结:整个tint基于setColorFilter实现,v4包实现兼容
附加不同shape类型 dye时 的代码:
private void drawableColor() {
try {
int color = getResources().getColor(R.color.colorAccent);
//如果是单层shape 则对应 GradientDrawable
Drawable background = loanBtn.getBackground();
if (background instanceof GradientDrawable) {
GradientDrawable gd = (GradientDrawable) background;
gd.setColor(color);
// 如果是多层叠加的 shape 则对应 StateListDrawable
} else if(background instanceof StateListDrawable) {
StateListDrawable sd = (StateListDrawable) background;
sd.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
} catch (Exception e) {
e.printStackTrace();
}
}
最后提下setColorFilter 如何渲染的(同 tint 里基本一致,间接证明两者的相关性)
@Override
public void setColorFilter(ColorFilter colorFilter) {
mBitmapState.mPaint.setColorFilter(colorFilter);
/** 绘制 v4版本的tint源码里最后也是如此,
但非兼容版本中是单独设置color 和mode 并不会触发invalidate,
而是在执行完updateStateList后执行这句代码
*/
invalidateSelf();
}