6

Я использовал метод ниже, чтобы правильно подобрать составные чертежи с помощью android.support.design 23.0.1. Теперь, когда они выпустили 23.1.0, он больше не работает на api LVL16, все мои чертежи черные.Android AppCompat 23.1.0 Tint Compound Drawable

У кого-нибудь есть предложение?

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); 
    } 

Спасибо.

+1

проверить [этот ответ] (http://stackoverflow.com/a/35867517/2826147) для обновления. –

+0

Код от Филиппа Дэвида работает, но из моего опыта вы должны написать 'wrap = wrap.mutate();' перед 'DrawableCompat.setTint()'. В противном случае он не будет работать должным образом, поскольку исходный файл будет изменен. – marius

ответ

8

Я столкнулся с той же проблемой на прошлой неделе, и, как оказалось, в AppCompatTextView v23.1.0, составные чертежи автоматически тонируются.

Вот решение, которое я нашел, с более подробными объяснениями, почему я сделал это ниже. Его не очень чистый, но, по крайней мере, он позволяет вам подкрасить ваши составные чертежи!

РЕШЕНИЕ

Поместите этот код в вспомогательном классе или в пользовательских TextView/Кнопка:

/** 
* 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(); 
    } 
} 

Тогда вы можете вызвать removeDefaultTinting(this); на каждом конструкторе класса, простирающейся AppCompatTextView или AppCompatButton. Например:

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

С этим код, работающий с v23.0.1, должен работать на v23.1.0.

Меня не устраивает использование отражения для изменения атрибутов в AppCompat lib, но это единственный способ, которым я нашел использование тонировки на составных чертежах с помощью v23.1.0. Надеемся, что кто-то найдет лучшее решение, или составные тонированные цвета будут добавлены в общедоступные методы AppCompat.

UPDATE

Я нашел еще более простое решение: эта ошибка возникает, только если вы установили составные XML с помощью вводимого коэффициента. Не устанавливайте их в xml, а затем установите их в свой код, и он будет работать. Неисправный код, находящийся в конструкторе, устанавливающий чертежи после его вызова, не затрагивается.

экспликации

В AppCompatTextView конструктор, текстовый помощник инициализируется:

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

В функции TextHelper loadFromAttributes, список Оттенок создается для каждого соединения Drawable. Как вы можете видеть, значение mDrawableXXXTint.mHasTintList всегда равно true. mDrawableXXXTint.mTintList - цвет оттенка, который будет применяться, и получить только от жестко заданных значений AppCompat. Для ваших пользовательских чертежей всегда будет null. Таким образом, вы получаете оттенок, имеющий нулевой «список оттенков».

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(); 
    } 
} 

Итак, если применить оттенок к сложному Drawable, а затем вызвать супер метод, такой как view.setCompoundDrawablesRelativeWithIntrinsicBounds, текстовый помощник применит свой нулевой оттенок к вашему рисунку, удалив все, что вы сделали ...

Наконец, здесь есть функция нанесения оттенка:

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

TintInfo параметрами является mDrawableXXXTint атрибутом класса TextHelper. Как вы можете видеть, если оно равно null, никакого оттенка не применяется. Установка всех атрибутов drawing tint на null запрещает AppCompat применять оттенок и позволяет вам делать то, что вы хотите, с помощью drawables.

Я не нашел чистого способа блокировать это поведение или заставить его применять оттенок, который я хочу. Все атрибуты являются частными, без геттеров.

+0

Вау. Не тот ответ, который я думал, что я получу! Попробуй свое решение, но такая работа - позор для Android ... как вы думаете, было бы разумно открыть ошибку для google, чтобы проверить? Большое спасибо :) –

+3

Добро пожаловать! Я потратил полдня, используя отладчик, чтобы понять, почему мои чертежи были белыми, поэтому, когда я увидел ваш вопрос, я чувствовал себя обязанным создать учетную запись и опубликовать то, что нашел :) Возможно, это хорошая идея, чтобы открыть ошибку, я просто сделал Не спешите. Это временное решение, которое, вероятно, перестанет работать в следующий раз, когда они изменят AppCompat. Код, который они используют, чтобы подкрасить составные чертежи, не слишком длинный или сложный, его просто полностью недоступный из внешнего класса. Простой сеттер для оттенка каждого составного рисунка, и он исправлен ... –

+1

Готово к выпуску. Со вчерашнего дня я узнал, что этот lib также разбивает TransitionDrawable на каком-то устройстве :) https://code.google.com/p/android/issues/detail?id=191111 –