6

Ich habe die folgende Methode verwendet, um zusammengesetzte Zeichen mit android.support.design 23.0.1 richtig zu tönen. Jetzt, da sie 23.1.0 veröffentlicht haben, funktioniert es nicht mehr auf api LVL16, alle meine Drawables sind schwarz.Android AppCompat 23.1.0 Tint Compound Drawable

Jeder hat einen Vorschlag?

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

Danke.

+1

Überprüfen Sie [diese Antwort] (http://StackOverflow.com/a/35867517/2826147) für die Aktualisierung. –

+0

Der Code von Philippe David funktioniert, aber aus meiner Erfahrung sollten Sie 'wrap = wrap.mutate();' vor 'DrawableCompat.setTint()' schreiben. Andernfalls wird es nicht ordnungsgemäß funktionieren, da das ursprüngliche Zeichen geändert wird. – marius

Antwort

8

Ich hatte das gleiche Problem letzte Woche, und es stellt sich heraus in der AppCompatTextView v23.1.0, zusammengesetzte Zeichen werden automatisch getönt.

Hier ist die Lösung, die ich gefunden habe, mit mehr Erklärungen, warum ich das unten getan habe. Es ist nicht sehr sauber, aber zumindest können Sie Ihre zusammengesetzten Zeichen tönen!

SOLUTION

Diesen Code in einer Hilfsklasse oder in Ihrer benutzerdefinierten 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(); 
    } 
} 

Dann Sie removeDefaultTinting(this); auf jedem Konstruktor Ihrer Klasse erweitern AppCompatTextView oder AppCompatButton aufrufen können. Zum Beispiel:

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

Mit diesem Code mit v23.0.1 arbeiten sollte auf v23.1.0 arbeiten.

Ich bin nicht zufrieden mit der Verwendung von Reflexion, um Attribute in der AppCompat-Bibliothek zu ändern, aber dies ist die einzige Möglichkeit, Tönung auf Compound-Zeichen mit v23.1.0 zu verwenden. Hoffentlich wird jemand eine bessere Lösung finden, oder Compound Drawable Tönung wird den öffentlichen AppCompat-Methoden hinzugefügt.

UPDATE

fand ich eine andere einfachere Lösung: Dieser Fehler tritt nur auf, wenn Sie Verbindung Drawables mit xml gesetzt. Setzen Sie sie nicht in XML, und setzen Sie sie in Ihrem Code und es wird funktionieren. Der fehlerhafte Code, der sich im Konstruktor befindet, wird nicht beeinflusst, wenn Sie Zeichnungsobjekte festlegen, nachdem sie aufgerufen wurden.

Explikationen

In AppCompatTextView Konstruktor, ein Text-Helfer initialisiert wird:

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

In der TextHelper loadFromAttributes Funktion wird eine Tönung Liste für jede Verbindung ziehbar erstellt. Wie Sie sehen können, ist mDrawableXXXTint.mHasTintList immer auf True eingestellt. mDrawableXXXTint.mTintList ist die Farbtonfarbe, die angewendet wird und nur von fest codierten Werten von AppCompat abgerufen wird. Für Ihre benutzerdefinierten Zeichen ist es immer Null. Sie haben also eine Tönung mit einer Null-Tint-Liste.

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

... 

Das Problem ist, dass diese Färbung im Konstruktor angelegt wird, und jedes Mal, wenn ein ziehbar eingestellt oder geändert wird:

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

Wenn Sie also eine Tönung zu einer Verbindung ziehbar anwenden, und dann rufen eine super Methode wie view.setCompoundDrawablesRelativeWithIntrinsicBounds, der Text-Helper wird seinen Null-Farbton auf Ihre Zeichen anwenden, alles entfernen, was Sie getan haben ...

Schließlich ist hier die Funktion des Farbtons Anwendung:

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

Die TintInfo in Parameter ist die mDrawableXXXTint Attribut der texthelper Klasse. Wie Sie sehen können, wird kein Farbton angewendet, wenn es null ist. Wenn Sie alle Attribute für die Zeichnungsfarbe auf null setzen, wird verhindert, dass AppCompat den Farbton anwendet, und Sie können die gewünschten Einstellungen für die Zeichnungsobjekte vornehmen.

Ich habe keine saubere Möglichkeit gefunden, dieses Verhalten zu blockieren oder die gewünschte Farbe aufzutragen. Alle Attribute sind privat, ohne Getter.

+0

Wow. Nicht die Art von Antwort, die ich dachte, ich würde bekommen! Versuchen Sie Ihre Lösung, aber diese Art der Arbeit ist eine Schande für Android ... denken Sie, es wäre klug, einen Bug für Google zu öffnen, um zu überprüfen? Vielen Dank :) –

+3

Gern geschehen! Ich habe einen halben Tag mit dem Debugger verbracht, um zu verstehen, warum meine Drawables weiß waren. Als ich deine Frage sah, fühlte ich mich verpflichtet, einen Account zu erstellen und zu posten. Es ist wahrscheinlich eine gute Idee, einen Bug zu öffnen Nimm dir die Zeit. Dies ist eine temporäre Lösung, die beim nächsten Ändern von AppCompat wahrscheinlich nicht mehr funktioniert. Der Code, den sie verwenden, um die zusammengesetzten Zeichen zu färben, ist nicht zu lang oder kompliziert, sie ist von einer externen Klasse nur völlig unzugänglich. Ein einfacher Setter für die Tönung jeder Verbindung zeichnete, und es ist behoben ... –

+1

Für das Problem erledigt. Seit gestern habe ich herausgefunden, dass diese Lib auch TransitionDrawable auf einem Gerät bricht :) https://code.google.com/p/android/issues/detail?id=191111 –