Android AppCompat 23.1.0着色化合物可拉伸

Android AppCompat 23.1.0着色化合物可拉伸,android,android-support-library,android-drawable,android-support-design,Android,Android Support Library,Android Drawable,Android Support Design,我使用了下面的方法,使用android.support.design 23.0.1对复合绘图板进行适当着色。现在他们发布了23.1.0,它在api LVL16上不再工作了,我所有的绘图工具都是黑色的 有人有什么建议吗 private void setCompoundColor(TextView view) { Drawable drawable = view.getCompoundDrawables()[0]; Drawable wrap = DrawableCompat.w

我使用了下面的方法,使用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中,复合绘图自动着色

这是我找到的解决方案,下面将详细说明我为什么这么做。它不是很干净,但至少它能让你给你的复合拖布上色

解决方案

将此代码放入助手类或自定义文本视图/按钮中:

/**
 * 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();
    }
}
然后可以调用
removedefaulttining(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中设置它们,然后在代码中设置它们,它就会工作。构造函数中的错误代码,在调用它之后设置可绘制项不受影响

解释

在AppCompatTextView构造函数中,初始化文本帮助程序:

mTextHelper.loadFromAttributes(attrs, defStyleAttr);
mTextHelper.applyCompoundDrawablesTints();
在TextHelper
loadFromAttributes
函数中,为每个复合绘图表创建一个着色列表。如您所见,
mdrawablexxtint.mHasTintList
始终设置为true
mdrawablexxtint.mTintList
是将应用的着色颜色,仅从AppCompat的硬编码值中获取。对于自定义绘图,它将始终为空。因此,最终得到的色调具有空的“色调列表”

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

 @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());
    }
}
参数中的
tintintinfo
是texthelper类的
mdrawablexxtint
属性。如您所见,如果为null,则不应用任何着色。将所有可绘制的着色属性设置为null可防止AppCompat应用其着色,并使您可以对可绘制的内容执行任何操作


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

您可以尝试这样的方法

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

哇!不是我想得到的那种回答!我将尝试你的解决方案,但这种解决方案对Android来说是一种耻辱。。。你认为打开一个bug让谷歌检查是明智的吗?非常感谢:)不客气!我花了半天的时间使用调试器来理解为什么我的绘图是白色的,所以当我看到你的问题时,我觉得有必要创建一个帐户并发布我的发现:)打开一个bug可能是个好主意,我只是没有花时间。这是一个临时解决方案,下次修改AppCompat时可能会停止工作。他们用来着色复合绘图表的代码并不太长或复杂,只是可以从外部类完全访问。一个简单的设置器,用于每个复合可拉丝的色调,并且它是固定的…针对该问题完成。从昨天开始,我发现这个库在某些设备上也破坏了TransitionDrawable:)AppCompatAutoCompleteTextView不存在mTextHelper?使用不将它们放入xml的方法可以完美地工作!检查更新。Philippe David的代码可以工作,但根据我的经验,您应该编写
wrap=wrap.mutate()
DrawableCompat.setTint()之前。否则,它将无法正常工作,因为原始可拉丝将被修改。
ContextCompat.getDrawable(context, R.drawable.cool_icon)?.apply {
    setTint(ContextCompat.getColor(context, R.color.red))
}