Ho affrontato lo stesso problema la scorsa settimana e in AppCompatTextView v23.1.0 risulta che i drawable composti sono automaticamente colorati.
Ecco la soluzione che ho trovato, con più spiegazioni sul motivo per cui ho fatto questo di seguito. Non è molto pulito ma almeno ti permette di colorare i tuoi drawable composti!
SOLUZIONE
inserire questo codice in una classe di supporto o nel vostro TextView custom/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();
}
}
quindi è possibile chiamare removeDefaultTinting(this);
su ogni costruzione della classe di estendere AppCompatTextView o AppCompatButton. Ad esempio:
public MyCustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
removeDefaultTinting(this);
}
Con questo, il codice che funziona con v23.0.1 dovrebbe funzionare su v23.1.0.
Non sono soddisfatto dall'uso del reflection per modificare gli attributi nella lib di AppCompat, ma questo è l'unico modo in cui ho trovato la colorazione sui drawable composti con v23.1.0. Si spera che qualcuno troverà una soluzione migliore, o una colorazione combinabile composta verrà aggiunta ai metodi pubblici AppCompat.
UPDATE
ho trovato un'altra soluzione più semplice: questo bug si verifica solo se si imposta drawable composti usando XML. Non impostarli in xml, quindi impostarli nel codice e funzionerà. Il codice errato nel costruttore, che imposta i drawable dopo che è stato chiamato non è influenzato.
spiegazioni
nel costruttore AppCompatTextView, un aiutante testo viene inizializzato:
mTextHelper.loadFromAttributes(attrs, defStyleAttr);
mTextHelper.applyCompoundDrawablesTints();
Nella funzione TextHelper loadFromAttributes
, un elenco tinta viene creato per ogni drawable composto. Come puoi vedere, mDrawableXXXTint.mHasTintList
è sempre impostato su true. mDrawableXXXTint.mTintList
è il colore della tinta che verrà applicato e viene ottenuto solo dai valori hardcoded di AppCompat. Per i tuoi drawable personalizzati, sarà sempre nullo. Quindi si finisce con una tinta che ha un "elenco tinta" nullo.
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));
}
...
Il problema è che questa tinta è applicata nel costruttore, e ogni volta che un disegnabile è impostato o modificato:
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (mBackgroundTintHelper != null) {
mBackgroundTintHelper.applySupportBackgroundTint();
}
if (mTextHelper != null) {
mTextHelper.applyCompoundDrawablesTints();
}
}
Quindi, se si applica una tinta a un drawable composto, e quindi chiamare un super metodo come view.setCompoundDrawablesRelativeWithIntrinsicBounds
, l'helper di testo applicherà la sua tinta nulla al tuo drawable, rimuovendo tutto ciò che hai fatto ...
Infine, ecco la funzione di applicare la tinta:
final void applyCompoundDrawableTint(Drawable drawable, TintInfo info) {
if (drawable != null && info != null) {
TintManager.tintDrawable(drawable, info, mView.getDrawableState());
}
}
Il TintInfo
parametri è l'attributo mDrawableXXXTint
della classe texthelper. Come puoi vedere, se è nullo, non viene applicata alcuna tinta. L'impostazione di tutti gli attributi di tinta drawable su null impedisce ad AppCompat di applicare la tinta e consente di eseguire le operazioni desiderate con i drawable.
Non ho trovato un modo pulito per bloccare questo comportamento o per ottenere il colore desiderato. Tutti gli attributi sono privati, senza getter.
controllare [questa risposta] (http://stackoverflow.com/a/35867517/2826147) per l'aggiornamento. –
Il codice di Philippe David funziona, ma dalla mia esperienza dovresti scrivere 'wrap = wrap.mutate();' prima di 'DrawableCompat.setTint()'. Altrimenti non funzionerà correttamente poiché il drawable originale verrà modificato. – marius