Reputation: 12627
logging my app my Logcat says: WARN: ... has unresolved theme attributes! Consider using Resources.getDrawable(int, Theme) or Context.getDrawable(int).
the code which produces the warning:
if (mCards.get(position).isFavorite()) {
viewHolder.mIbStar
.setImageDrawable(mContext.getResources()
.getDrawable(R.drawable.btn_is_fav));
} else {
viewHolder.mIbStar
.setImageDrawable(mContext.getResources()
.getDrawable(R.drawable.btn_isnt_fav));
}
I searched for this log, but didn't find anything useful. How knows whats the issue? The program is stable, so no Nullpointer..
Thanks in advance
Upvotes: 10
Views: 11148
Reputation: 648
I met this issue too. And I tried to find out the solution from the source code.
The log was printed at,
// android.content.res.Resources#getDrawable(int)
public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
final Drawable d = getDrawable(id, null);
if (d != null && d.canApplyTheme()) {
Log.w(TAG, "Drawable " + getResourceName(id) + " has unresolved theme "
+ "attributes! Consider using Resources.getDrawable(int, Theme) or "
+ "Context.getDrawable(int).", new RuntimeException());
}
return d;
}
It's a waring log, and if the canApplyTheme
method of the Drawable returns true, then it will print the log.
So, different kinds of drawable will have different implementations of this method.
In my case, I used a GradientDrawable (defined by the xml drawable). So, let's have a look at the method of GradientDrawable.
// android.graphics.drawable.GradientDrawable#canApplyTheme
@Override
public boolean canApplyTheme() {
return (mGradientState != null && mGradientState.canApplyTheme()) || super.canApplyTheme();
}
The GradientDrawable tries to get the value from canApplyTheme
method of mGradientState
. The mGradientState
is a GradientState.
As for GradientState, its canApplyTheme
is defined as below,
// android.graphics.drawable.GradientDrawable.GradientState#canApplyTheme
@Override
public boolean canApplyTheme() {
boolean mGradientColorState = mGradientColors != null;
if (mGradientColors != null) {
for (int i = 0; i < mGradientColors.length; i++) {
mGradientColorState |= (mGradientColors[i] != null && mGradientColors[i]
.canApplyTheme());
}
}
return mThemeAttrs != null
|| mAttrSize != null || mAttrGradient != null
|| mAttrSolid != null || mAttrStroke != null
|| mAttrCorners != null || mAttrPadding != null
|| (mTint != null && mTint.canApplyTheme())
|| (mStrokeColors != null && mStrokeColors.canApplyTheme())
|| (mSolidColors != null && mSolidColors.canApplyTheme())
|| mGradientColorState
|| super.canApplyTheme();
}
So, if mThemeAttrs
is not null, then the canApplyTheme
method will return true.
As for mThemeAttrs
, it's derived from extractThemeAttrs
method of TypedArray. In the extractThemeAttrs
method,
// android.content.res.TypedArray#extractThemeAttrs(int[])
public int[] extractThemeAttrs(@Nullable int[] scrap) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
int[] attrs = null;
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
final int index = i * STYLE_NUM_ENTRIES;
if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
// Not an attribute, ignore.
continue;
}
// ....
}
So, obviously, if your resources has a TypedValue.TYPE_ATTRIBUTE typed attribute, then the log will print.
What is TypedValue.TYPE_ATTRIBUTE
, is an attribute such as ?attr:xxxx
. So, the solution is:
First, try to remove the ?attr
values from your drawable. Then, if it's necessary for you, you have to replace the Resource.getDrawable(Int)
method by Context.getDrwable(Int)
.
Since, the ?attr
is associated with a theme. The Android system need to read real value from the theme. While the Resource.getDrwable(Int)
use a null
theme, so, the system is unable to get real value for your ?attr
.
If you are unable to use the Context.getDrawable(Int)
method, since it's added in API 21. You can use the ContextCompat.getDrawable(Context, Int)
method instead.
Upvotes: 0
Reputation: 940
The problem is that the system can't find the associated theme to resolve these attributes values. That's why the logcat suggest you to use the Resources.getDrawable(int, Theme) method providing the theme, or to use the Context.getDrawable(int) method where the Context will use its current theme :
Return a drawable object associated with a particular resource ID and styled for the current theme.
Moreover, according to the Android official documentation : http://developer.android.com/reference/android/content/res/Resources.html#getDrawable(int)
This method was deprecated in API level 22. Use getDrawable(int, Theme) instead.
and more specifically
Note: To obtain a themed drawable, use Context.getDrawable(int) or getDrawable(int, Theme) passing the desired theme.
Consider replacing
mContext.getResources().getDrawable(int)
by
mContext.getDrawable(int)
such as proposes it the logcat.
Upvotes: 11