基于 tint 动态修改shape 颜色 以及原理

简单描述使用细节场景,根据界面颜色值动态切换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();
    }


你可能感兴趣的:(基于 tint 动态修改shape 颜色 以及原理)