Logcat says: "Resource has unresolved theme attributes"
Asked Answered
M

2

10

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

Moultrie answered 8/3, 2015 at 21:49 Comment(4)
I do not really know what the issue is but the logcat output seems quite clear. You have to replace mContext.getResources().getDrawable with mContext.getDrawableTriton
thanks for your fast answer. I tried this earlier, but this method is for API >= 21... So maybe I will provide a method which loads the content for older versions.Moultrie
now I changed the code to: viewHolder.mIbStar.setBackgroundResource(R.drawable.btn_is_fav); seems to do the same.. ripple effect is still visible.. :)Moultrie
Okay nice to hear that you worked it out. setBackgroundResource is the best solution, have not thought about that yesterday.Triton
N
11

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.

Nutritious answered 27/5, 2015 at 12:20 Comment(3)
But context.getDrawable(int) requires min API level 21Sidon
Indeed, I did not focus on this point. So, in this case, the best solution, as mentioned in comments by @ByteHamster, is to use setBackgroundResourceNutritious
Try to use ContextCompat. It provides backward compatibility to API 4.Strenuous
S
0

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.

Sharlenesharline answered 16/1 at 9:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.