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.
mContext.getResources().getDrawable
withmContext.getDrawable
– TritonsetBackgroundResource
is the best solution, have not thought about that yesterday. – Triton