6

In android I am looping through the database and assigning text and image:

Cursor res = myDb.getAllData();

while (res.moveToNext()) {
    Actors actor = new Actors();
    actor.setName(res.getString(1));

    String th =  res.getString(11);
    Integer thumb = this.getResources().getIdentifier(th, "drawable", "mypackage");

    actor.setThumb(R.drawable.th);
}

However Lint suggests not to use getIdentifier - Use of this function is discouraged because resource reflection makes it harder to perform build optimizations and compile-time verification of code.

In database column I have just the image name (string). How can I replace getIdentifier?

Even if I change the DB column maybe directly to R.drawable.imagename, it is still a string and for setThumb I need a drawable.

Lino
  • 5,084
  • 3
  • 21
  • 39
Darksymphony
  • 2,155
  • 30
  • 54
  • How many images are there? You could use a `switch` statement, or build and cache a `Map` of string-to-identifer values and use that. – CommonsWare Jan 24 '23 at 21:45
  • there are around 200 images. They are all in drawable folder, I just want to list them according to their name in database. I know I can use also Picasso library, but rather no additional libraries. For now, getIdentifier works well, but as I mentioned, it is not suggested to use. – Darksymphony Jan 25 '23 at 18:27
  • I believe It's time to do database migration from strings to integers as a first step; then everything will be straightforward – Zain Jan 29 '23 at 14:28
  • and what will be in the database columns as integer? I need the image name there. Even if you have an integer in database, you can't use it dynamically in code as drawable, because drawable is drawable, unless you store the drawable ID in database, which I don't know for all 200 images and will be more in the future – Darksymphony Jan 30 '23 at 13:18
  • You'd store the drawables into an XML array or even java array (in the same order as the loop); and store that integer (each drawable order) into the database instead of the strings. – Zain Jan 30 '23 at 23:16
  • hmm, maybe not a bad idea, but for what I use database if I will store those drawables also in another place... but if there is no other way, at least seems a possible workaround. Would be great if you post an answer with such an example, how did you mean it exactly – Darksymphony Jan 31 '23 at 18:55
  • 1
    Just saw you did find a solution through reflection, viola!. No need to put an answer I guess; if you still need it please drop me a tag. – Zain Feb 01 '23 at 11:18

5 Answers5

1

The best way is Using mapOf list like This example

val iconResourceMap = mapOf(
        "icon_1" to R.drawable.icon_1,
        "icon_2" to R.drawable.icon_2,
        // Add mappings for all the icons you have
    )

    for (i in 0 until data.length()) {
        val iconName = "icon_${i + 1}"
        val iconResId = iconResourceMap[iconName] ?: 0
}

Happy coding!

Mohammed Qadah
  • 75
  • 1
  • 10
0

Ok, so the only solution what I've found is here https://stackoverflow.com/a/4428288/1345089

public static int getResId(String resName, Class<?> c) {

        try {
            Field idField = c.getDeclaredField(resName);
            return idField.getInt(idField);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        } 
    }

and then just calling:

int resID = getResId("icon", R.drawable.class);

This works very well, however some users reports, that after installing the app from Play store (not my app, but any with this method implemented and proguard enabled), after a while it will start throwing NoSuchFieldException, as more resources are mapped to the same class/field.

It can be caused by proguard but not sure. The solution is then to put the old way getResources().getIdentifier to the exception part of code.

Darksymphony
  • 2,155
  • 30
  • 54
  • 6
    But this uses direct reflection, so it's even hackier that your original solution. – bongo Feb 01 '23 at 11:47
  • well, I did not find another solution, and some blogs posted this is about 5 times faster than with getIdentifier. I don't know about any other solution – Darksymphony Feb 01 '23 at 17:01
  • 1
    could you please share the blog post link where they state this approach is 5 times faster? thanks – Lino Feb 03 '23 at 09:50
  • For example here http://daniel-codes.blogspot.com/2009/12/dynamically-retrieving-resources-in.html – Darksymphony Feb 04 '23 at 13:23
  • @Darksymphony it might be faster but it's unreliable and error-prone, please don't use it! For example it will fail for if you have `` the actual field name will be `string_name`. But the bigger issue it will fail in production (release build) if [resource/code shrinking](https://developer.android.com/build/shrink-code) is enabled (and Google encourages to enable it). Instead of this method you should just map string names to integer ids in the code, or if **absolutely needed** use the `Resources.getIdentifier` combined with the `@SuppressLint("DiscouragedApi")` – Volo Jul 27 '23 at 17:05
0

You can try this approach:

  • Rename your resources as follow: ressourceName00 ressourceName01 ressourceName02 .... and so on,

then use the methode below:

for (int i = 0; i < RessourceQtt; i++) {
        Uri path1 = Uri.parse("android.resource://yourPackage Directories/drawable/ressourceName0" + i);

        list.add(new CarouselItem(String.valueOf(path1)));
    }
Djoxy
  • 54
  • 4
0

You can try this my code three way

// Show message and quit
    Application app = cordova.getActivity().getApplication();
    String package_name = app.getPackageName();
    Resources resources = app.getResources();
    String message = resources.getString(resources.getIdentifier("message", "string", package_name));
    String label = resources.getString(resources.getIdentifier("label", "string", package_name));
    this.alert(message, label);

set icon like that

private int getIconResId() {
Context context = getApplicationContext();
Resources res = context.getResources();
String pkgName = context.getPackageName();
int resId;
resId = res.getIdentifier("icon", "drawable", pkgName);
return resId;

}

this is also

//set icon image
final String prefix = "ic_";
String icon_id = prefix + cursor.getString(cursor.getColumnIndex(WeatherEntry.COLUMN_ICON));
Resources res = mContext.getResources();
int resourceId = res.getIdentifier(icon_id, "drawable", mContext.getPackageName());
viewHolder.imgIcon.setImageResource(resourceId);

I hope this code help for you.

public static int getIcId(String resN, Class<?> c) {
try {
    Field idF = c.getDeclaredField(resN);
    return idF.getInt(idF);
} catch (Exception e) {
    throw new RuntimeException("No resource ID found for: "
            + resN+ " / " + c, e);
}}
0

In Kotlin, due to getidentifier and Typed array

instate of this code:

resources.getIdentifier("l$i", "drawable", packageName)

must do two step:

1- Make a array list in res/values/arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="icons">
        <item>@drawable/home</item>
        <item>@drawable/settings</item>
        <item>@drawable/logout</item>
    </array>
 
</resources>

2- Call in an Activity

val icons: TypedArray = resources.obtainTypedArray(R.array.icons)
val drawable: Drawable = icons.getDrawable(0)

Points:

I) if you want call ID as Int use this way :

icons.getResourceId(i,0) //i is a counter

II) if you use it in Recycle View so must add

 icons.recycle()
Mori
  • 2,653
  • 18
  • 24