【问题标题】:Android AppCompat 23.1.0 Tint Compound DrawableAndroid AppCompat 23.1.0 Tint Compound Drawable
【发布时间】:2015-10-19 18:18:18
【问题描述】:

我使用下面的方法使用 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);
  }

谢谢。

【问题讨论】:

  • 检查this answer 以获取更新。
  • Philippe David 的代码有效,但根据我的经验,您应该在 DrawableCompat.setTint() 之前编写 wrap = wrap.mutate();。否则它将无法正常工作,因为原始可绘制对象将被修改。

标签: android android-support-library android-drawable android-support-design


【解决方案1】:

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

这是我找到的解决方案,下面详细说明了我为什么这样做。它不是很干净,但至少它可以让你为你的复合绘图着色!

解决方案

将此代码放入帮助程序类或您的自定义 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不受影响。

解释

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

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

在 TextHelper loadFromAttributes 函数中,为每个复合可绘制对象创建一个色调列表。如您所见,mDrawableXXXTint.mHasTintList 始终设置为 true。 mDrawableXXXTint.mTintList 是要应用的色调颜色,只能从 AppCompat 的硬编码值中获取。对于您的自定义可绘制对象,它将始终为空。所以你最终会得到一个具有空“色调列表”的色调。

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 之类的超级方法,则文本助手会将其空色调应用于您的可绘制对象,从而删除您所做的一切...

最后,这是应用 tint 的函数:

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

参数中的TintInfo 是texthelper 类的mDrawableXXXTint 属性。如您所见,如果它为空,则不应用色调。将所有可绘制的色调属性设置为 null 可防止 AppCompat 应用其色调,并使您能够对可绘制对象进行任何您想要的操作。

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

【讨论】:

  • 哇。不是我想我会得到的那种答案!会尝试你的解决方案,但这种解决方法对 Android 来说是一种耻辱......你认为打开一个 bug 让谷歌检查是否明智?非常感谢:)
  • 不客气!我花了半天时间使用调试器来了解为什么我的可绘制对象是白色的,所以当我看到你的问题时,我觉得有必要创建一个帐户并发布我发现的内容:) 打开一个错误可能是个好主意,我只是没有'不要花时间。这是一个临时解决方案,下次他们修改 AppCompat 时可能会停止工作。他们用来为复合可绘制对象着色的代码不会太长或太复杂,只是完全无法从外部类访问。每个复合可绘制对象的色调的简单设置器,它是固定的......
  • 解决了这个问题。从昨天开始,我发现这个库也在某些设备上破坏了 TransitionDrawable :) code.google.com/p/android/issues/detail?id=191111
  • AppCompatAutoCompleteTextView 的 mTextHelper 不存在?
  • 使用不放入xml的方法效果很好!
【解决方案2】:

你可以试试这样的

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-29
    • 2013-10-07
    • 1970-01-01
    • 2014-11-05
    • 1970-01-01
    • 1970-01-01
    • 2019-10-02
    • 2015-08-24
    相关资源
    最近更新 更多