Android AppCompat 23.1.0彩色复合绘制



我使用下面的方法来正确地使用android.support.design 23.0.1的复合绘图。现在他们发布了23.1.0,它在api LVL16上不再工作了,我所有的画都是黑色的。

谁有什么建议?

  private void setCompoundColor(TextView view) {
    Drawable drawable = view.getCompoundDrawables()[0];
    Drawable wrap = DrawableCompat.wrap(drawable);
    DrawableCompat.setTint(wrap, ContextCompat.getColor(this, R.color.primaryLighter2));
    DrawableCompat.setTintMode(wrap, PorterDuff.Mode.SRC_IN);
    wrap = wrap.mutate();
    view.setCompoundDrawablesRelativeWithIntrinsicBounds(wrap, null, null, null);
  }

谢谢。

我上周遇到了同样的问题,结果在AppCompatTextView v23.1.0中,复合绘制是自动着色的。

这是我找到的解决方案,下面有更多关于我为什么这样做的解释。它不是很干净,但至少它使您能够对复合绘制着色!

<<p> 解决方案/strong>

把这段代码放在一个helper类中,或者在你的自定义TextView/Button中:

/**
 * The app compat text view automatically sets the compound drawable tints for a static array of drawables ids.
 * If the drawable id is not in the list, the lib apply a null tint, removing the custom tint set before.
 * There is no way to change this (private attributes/classes, only set in the constructor...)
 *
 * @param object the object on which to disable default tinting.
 */
public static void removeDefaultTinting(Object object) {
    try {
        // Get the text helper field.
        Field mTextHelperField = object.getClass().getSuperclass().getDeclaredField("mTextHelper");
        mTextHelperField.setAccessible(true);
        // Get the text helper object instance.
        final Object mTextHelper = mTextHelperField.get(object);
        if (mTextHelper != null) {
            // Apply tint to all private attributes. See AppCompat source code for usage of theses attributes.
            setObjectFieldToNull(mTextHelper, "mDrawableStartTint");
            setObjectFieldToNull(mTextHelper, "mDrawableEndTint");
            setObjectFieldToNull(mTextHelper, "mDrawableLeftTint");
            setObjectFieldToNull(mTextHelper, "mDrawableTopTint");
            setObjectFieldToNull(mTextHelper, "mDrawableRightTint");
            setObjectFieldToNull(mTextHelper, "mDrawableBottomTint");
        }
    } catch (NoSuchFieldException e) {
        // If it doesn't work, we can do nothing else. The icons will be white, we will see it.
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // If it doesn't work, we can do nothing else. The icons will be white, we will see it.
        e.printStackTrace();
    }
}
/**
 * Set the field of an object to null.
 *
 * @param object    the TextHelper object (class is not accessible...).
 * @param fieldName the name of the tint field.
 */
private static void setObjectFieldToNull(Object object, String fieldName) {
    try {
        Field tintField;
        // Try to get field from class or super class (depends on the implementation).
        try {
            tintField = object.getClass().getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            tintField = object.getClass().getSuperclass().getDeclaredField(fieldName);
        }
        tintField.setAccessible(true);
        tintField.set(object, null);
    } catch (NoSuchFieldException e) {
        // If it doesn't work, we can do nothing else. The icons will be white, we will see it.
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // If it doesn't work, we can do nothing else. The icons will be white, we will see it.
        e.printStackTrace();
    }
}

然后你可以在你的类扩展AppCompatTextView或AppCompatButton的每个构造函数上调用removeDefaultTinting(this);。例如:

public MyCustomTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    removeDefaultTinting(this);
}

有了这个,在v23.0.1上工作的代码应该可以在v23.1.0上工作。

我不满意使用反射来改变AppCompat库中的属性,但这是我发现在v23.1.0的复合绘制上使用着色的唯一方法。希望有人能找到一个更好的解决方案,或者复合绘制着色将被添加到AppCompat的公共方法。

我发现了另一个更简单的解决方案:只有当您使用xml设置复合绘制时才会出现此错误。不要在xml中设置它们,然后在代码中设置它们,它将工作。错误的代码在构造函数中,在调用后设置drawables不受影响。

<<p> 论述/strong>

在AppCompatTextView构造函数中,初始化了一个文本帮助器:

mTextHelper.loadFromAttributes(attrs, defStyleAttr);
mTextHelper.applyCompoundDrawablesTints();

在TextHelper loadFromAttributes函数中,为每个可绘制的化合物创建一个颜色列表。如你所见,mDrawableXXXTint.mHasTintList总是被设置为true。mDrawableXXXTint.mTintList是将应用的色调颜色,并且只能从AppCompat的硬编码值中获得。对于您的自定义drawables,它将始终为空。所以你最终会得到一个带有空"tint list"的tint。

TypedArray a = context.obtainStyledAttributes(attrs, VIEW_ATTRS, defStyleAttr, 0);
    final int ap = a.getResourceId(0, -1);
    // Now read the compound drawable and grab any tints
    if (a.hasValue(1)) {
        mDrawableLeftTint = new TintInfo();
        mDrawableLeftTint.mHasTintList = true;
        mDrawableLeftTint.mTintList = tintManager.getTintList(a.getResourceId(1, 0));
    }
    if (a.hasValue(2)) {
        mDrawableTopTint = new TintInfo();
        mDrawableTopTint.mHasTintList = true;
        mDrawableTopTint.mTintList = tintManager.getTintList(a.getResourceId(2, 0));
    }
...

问题是这种色调是在构造函数中应用的,每次设置或更改可绘制对象时:

 @Override
protected void drawableStateChanged() {
    super.drawableStateChanged();
    if (mBackgroundTintHelper != null) {
        mBackgroundTintHelper.applySupportBackgroundTint();
    }
    if (mTextHelper != null) {
        mTextHelper.applyCompoundDrawablesTints();
    }
}

所以,如果你应用一个颜色复合绘制,然后调用一个超级方法,如view.setCompoundDrawablesRelativeWithIntrinsicBounds,文本助手将应用它的空色调到你的绘制,删除你所做的一切…

最后,下面是应用颜色的函数:

final void applyCompoundDrawableTint(Drawable drawable, TintInfo info) {
    if (drawable != null && info != null) {
        TintManager.tintDrawable(drawable, info, mView.getDrawableState());
    }
}

parameters中的TintInfo是texthelper类的mDrawableXXXTint属性。如您所见,如果它为null,则不应用任何颜色。将所有可绘制的颜色属性设置为null可以防止AppCompat应用它的颜色,并允许你对可绘制的内容做任何你想做的事情。

我没有找到一个干净的方法来阻止这种行为或让它应用我想要的颜色。所有属性都是私有的,没有getter。

你可以试试这样

ContextCompat.getDrawable(context, R.drawable.cool_icon)?.apply {
    setTint(ContextCompat.getColor(context, R.color.red))
}

最新更新