基于 tint 動態(tài)修改shape 顏色 以及原理

簡單描述使用細(xì)節(jié)場景,根據(jù)界面顏色值動態(tài)切換shape的背景色

這里想到了tint 但因為是動態(tài)修改,在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 方法 調(diào)用: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) {
//關(guān)注 這里的 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還是動態(tài)代碼都是基于這個,所以可以直接使用 如下:

//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能力【源碼即前面已經(jīng)提到的源碼中的 or 部分】
    Drawable wrapDrawable = DrawableCompat.wrap(drawable);
        wrapDrawable.setTint(color);

綜上總結(jié):整個tint基于setColorFilter實現(xiàn),v4包實現(xiàn)兼容

附加不同shape類型 dye時 的代碼:

 private void drawableColor() {
        try {
            int color = getResources().getColor(R.color.colorAccent);
            //如果是單層shape 則對應(yīng) GradientDrawable
            Drawable background = loanBtn.getBackground();
            if (background instanceof GradientDrawable) {
                GradientDrawable gd = (GradientDrawable) background;
                gd.setColor(color);
                // 如果是多層疊加的 shape 則對應(yīng) StateListDrawable
            } else if(background instanceof StateListDrawable) {
                StateListDrawable sd = (StateListDrawable) background;
                sd.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

最后提下setColorFilter 如何渲染的(同 tint 里基本一致,間接證明兩者的相關(guān)性)

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mBitmapState.mPaint.setColorFilter(colorFilter);
/** 繪制 v4版本的tint源碼里最后也是如此,
但非兼容版本中是單獨設(shè)置color 和mode 并不會觸發(fā)invalidate,
而是在執(zhí)行完updateStateList后執(zhí)行??這句代碼
*/
        invalidateSelf();
    }


?著作權(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)容