TextView的drawableTint兼容实现方法

在ImageView中有tint属性,用于设置图片的颜色,其实现原理是使用了ColorFilter的PorterDuffColorFilter,来重新生成drawable,关键代码是:读取tint,设置ColorFilter,生成新的Drawable,重新绘制

// 读取tint
int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0);
if (tint != 0) {
    setColorFilter(tint);
}
// 设置ColorFilter
public final void setColorFilter(int color) {
    setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
public final void setColorFilter(int color, PorterDuff.Mode mode) {
    setColorFilter(new PorterDuffColorFilter(color, mode));
}
// 生成新的Drawable并重新绘制
public void setColorFilter(ColorFilter cf) {
    if (mColorFilter != cf) {
        mColorFilter = cf;
        mColorMod = true;
        applyColorMod();
        invalidate();
    }
}
// 生成新的Drawable
private void applyColorMod() {
    // Only mutate and apply when modifications have occurred. This should
    // not reset the mColorMod flag, since these filters need to be
    // re-applied if the Drawable is changed.
    if (mDrawable != null && mColorMod) {
        mDrawable = mDrawable.mutate();
        mDrawable.setColorFilter(mColorFilter);
        mDrawable.setXfermode(mXfermode);
        mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
    }
}

我们看到5.1之前的版本默认的PorterDuff.Mode是SRC_ATOP,如果想设置mode的话必须使用java代码,所以在5.1新增了tintMode属性,而且默认。如果兼容5.1之前的版本可以使用support库或androidx库的AppCompatImageView,使用app:tint和app:tintMode属性即可。

而对于TextView来说,想要对DrawableLeft等做变换,在android6.0才有drawableTint和drawableTintMode属性(可能是忘了加),而support库的AppCompatTextView也一直没有添加对应的兼容属性,直到androidx的1.1.0版本才添加上

    <declare-styleable name="AppCompatTextView">
        …………
        <!-- Tint to apply to the compound (left, top, etc.) drawables. -->
        <attr format="color" name="drawableTint"/>
        <!-- Blending mode used to apply the compound (left, top, etc.) drawables tint. -->
        <attr name="drawableTintMode">
            <!-- The tint is drawn on top of the drawable.
                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
            <enum name="src_over" value="3"/>
            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
                 color channels are thrown out. [Sa * Da, Sc * Da] -->
            <enum name="src_in" value="5"/>
            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
            <enum name="src_atop" value="9"/>
            <!-- Multiplies the color and alpha channels of the drawable with those of
                 the tint. [Sa * Da, Sc * Dc] -->
            <enum name="multiply" value="14"/>
            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
            <enum name="screen" value="15"/>
            <!-- Combines the tint and drawable color and alpha channels, clamping the
                 result to valid color values. Saturate(S + D) -->
            <enum name="add" value="16"/>
        </attr>
    </declare-styleable>

那么,如果想使用DrawableTint就有这些方案:

1.使用的是androidx库1.1.0之后的版本,直接使用兼容控件AppCompatTextView。

2.无法使用AppCompatTextView的情况,只能通过判断版本,高版本设置属性,低版本则通过java代码来实现对drawable的变换,然后重新设置drawable left等。

    private void setDrawableLeft() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            textView.setCompoundDrawableTintList(getColorStateList(R.color.tint_color));
            // 如果需要设置 mode
            //textView.setCompoundDrawableTintMode(mode);
        } else {
            Drawable drawable = textView.getCompoundDrawables()[0];
            Drawable left = tintListDrawable(drawable, ContextCompat.getColorStateList(this, R.color.tint_color));
            textView.setCompoundDrawables(left,null,null,null);
        }
    }

    public Drawable tintListDrawable(@NonNull Drawable drawable, ColorStateList colors) {
        Drawable wrappedDrawable = getCanTintDrawable(drawable);
        // 进行着色
        DrawableCompat.setTintList(wrappedDrawable, colors);
        // 如果需要设置 mode
        //DrawableCompat.setTintMode(wrappedDrawable, mode);
        return wrappedDrawable;
    }

    @NonNull
    private Drawable getCanTintDrawable(@NonNull Drawable drawable) {
        // 获取此drawable的共享状态实例
        Drawable.ConstantState state = drawable.getConstantState();
        // 对drawable 进行重新实例化、包装、可变操作
        return DrawableCompat.wrap(state == null ? drawable : state.newDrawable()).mutate();
    }

注意的地方,对Drawable变换时生成的Drawable必须使用mutate,否则可能导致其他也使用当前图片的地方也会发生变换,因为Android系统加载资源,资源都是缓存的,对资源的改变是永久的。

发布了53 篇原创文章 · 获赞 17 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/jklwan/article/details/101604307