先週同じ問題に直面しましたが、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();
}
}
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 パブリック メソッドに複合描画可能な着色が追加されるでしょう。
アップデート
別のより簡単な解決策を見つけました。このバグは、xml を使用して複合ドローアブルを設定した場合にのみ発生します。それらをxmlに設定しないで、コードに設定すると機能します。コンストラクター内にある欠陥のあるコードは、呼び出された後にドローアブルを設定しても影響を受けません。
説明
AppCompatTextView コンストラクターでは、テキスト ヘルパーが初期化されます。
mTextHelper.loadFromAttributes(attrs, defStyleAttr);
mTextHelper.applyCompoundDrawablesTints();
TextHelperloadFromAttributes
関数では、複合ドローアブルごとにティント リストが作成されます。ご覧のとおり、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));
}
...
問題は、この色合いがコンストラクターで適用され、drawable が設定または変更されるたびに次のようになることです。
@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());
}
}
TintInfo
in パラメーターはmDrawableXXXTint
、texthelper クラスの属性です。ご覧のとおり、null の場合、色合いは適用されません。ドローアブルのすべてのティント属性を null に設定すると、AppCompat がそのティントを適用できなくなり、ドローアブルを自由に操作できるようになります。
この動作をブロックしたり、必要な色合いを適用したりするためのきれいな方法が見つかりませんでした。すべての属性はプライベートであり、ゲッターはありません。