25

In my Android project, I want to loop through the entire collection of Drawable resources. Normally, you can only retrieve a specific resource via its ID using something like:

InputStream is = Resources.getSystem().openRawResource(resourceId)

However, I want to get all Drawable resources where I won't know their ID's beforehand. Is there a collection I can loop through or perhaps a way to get the list of resource ID's given the resources in my project?

Or, is there a way for me in Java to extract all property values from the R.drawable static class?

Petter Friberg
  • 21,252
  • 9
  • 60
  • 109
Matt Huggins
  • 81,398
  • 36
  • 149
  • 218

11 Answers11

34

Okay, this feels a bit hack-ish, but this is what I came up with via Reflection. (Note that resources is an instance of class android.content.res.Resources.)

final R.drawable drawableResources = new R.drawable();
final Class<R.drawable> c = R.drawable.class;
final Field[] fields = c.getDeclaredFields();

for (int i = 0, max = fields.length; i < max; i++) {
    final int resourceId;
    try {
        resourceId = fields[i].getInt(drawableResources);
    } catch (Exception e) {
        continue;
    }
    /* make use of resourceId for accessing Drawables here */
}

If anyone has a better solution that makes better use of Android calls I might not be aware of, I'd definitely like to see them!

Matt Huggins
  • 81,398
  • 36
  • 149
  • 218
  • 5
    This is the only way to get resources (as opposed to assets) that I can find. Note that you don't need drawableResources. Since all the fields of R are static, getInt() can take null. – Steve Blackwell Dec 09 '10 at 05:09
  • 1
    Just a detail to note: `java.lang.reflect.Field` – Al Lelopath Jan 03 '14 at 22:45
  • At what point would this be called? I created a function that returns an image array but I am getting an error saying that the array is null therefore it has no length @SteveBlackwell – Pj Rigor Sep 29 '16 at 01:32
  • @PjRigor It's hard to know why you're getting that result without seeing some code. It's probably best to post a new question. – Steve Blackwell Sep 29 '16 at 05:39
  • This will only work for the resources in whatever `R` you import. If you have a multi-module project (e.g. `com.example.app.R` and `com.example.lib.R`) then you won't be able to see *all* resources this way. – tir38 Aug 19 '22 at 16:35
  • I'll add, you just have to import all Rs: `import com.examle.lib.R` and `import com.example.app.R as appR` etc – tir38 Aug 19 '22 at 16:49
8

I have taken Matt Huggins great answer and refactored it to make it more generic:

public static void loadDrawables(Class<?> clz){
    final Field[] fields = clz.getDeclaredFields();
    for (Field field : fields) {
        final int drawableId;
        try {
            drawableId = field.getInt(clz);
        } catch (Exception e) {
            continue;
        }
        /* make use of drawableId for accessing Drawables here */
    }   
}

Usage:

loadDrawables(R.drawable.class);
Jan-Terje Sørensen
  • 14,468
  • 8
  • 37
  • 37
8

i used getResources().getIdentifier to scan through sequentially named images in my resource folders. to be on a safe side, I decided to cache image ids when activity is created first time:

    private void getImagesIdentifiers() {

    int resID=0;        
    int imgnum=1;
    images = new ArrayList<Integer>();

    do {            
        resID=getResources().getIdentifier("img_"+imgnum, "drawable", "InsertappPackageNameHere");
        if (resID!=0)
            images.add(resID);
        imgnum++;
    }
    while (resID!=0);

    imageMaxNumber=images.size();
}
mishkin
  • 5,932
  • 8
  • 45
  • 64
7

You should use the Raw folder and AssetManager, but if you want to use drawables because why not, here is how...

Let's suppose we have a very long file list of JPG drawables and we want to get all the resource ids without the pain of retrieving one by one (R.drawable.pic1, R.drawable.pic2, ... etc)

//first we create an array list to hold all the resources ids
ArrayList<Integer> imageListId = new ArrayList<Integer>();

//we iterate through all the items in the drawable folder
Field[] drawables = R.drawable.class.getFields();
for (Field f : drawables) {
    //if the drawable name contains "pic" in the filename...
    if (f.getName().contains("image"))
        imageListId.add(getResources().getIdentifier(f.getName(), "drawable", getPackageName()));
}

//now the ArrayList "imageListId" holds all ours image resource ids
for (int imgResourceId : imageListId) {
     //do whatever you want here
}
D.Snap
  • 1,704
  • 1
  • 22
  • 15
6

Add a picture named aaaa and another named zzzz, then iterate through the following:

public static void loadDrawables() {
  for(long identifier = (R.drawable.aaaa + 1);
      identifier <= (R.drawable.zzzz - 1);
      identifier++) {
    String name = getResources().getResourceEntryName(identifier);
    //name is the file name without the extension, indentifier is the resource ID
  }
}

This worked for me.

Mikaël Mayer
  • 10,425
  • 6
  • 64
  • 101
4

If you find yourself wanting to do this you're probably misusing the resource system. Take a look at assets and AssetManager if you want to iterate over files included in your .apk.

adamp
  • 28,862
  • 9
  • 81
  • 69
3

I guess the reflection code will work but I don't understand why you need this.

Resources in Android are static once the application is installed so you can have a list of resources or an array. Something like:

<string-array name="drawables_list">
    <item>drawable1</item>
    <item>drawable2</item>
    <item>drawable3</item>
</string-array>

And from your Activity you can get it by doing:

getResources().getStringArray(R.array.drawables_list);
Macarse
  • 91,829
  • 44
  • 175
  • 230
  • 6
    This works, but the problem with it is that every time I add a drawable to the folder, I also have to update this string array. I'm looking for something more automatic. – Matt Huggins Jul 11 '10 at 16:57
2

Just do this:

Field[] declaredFields = (R.drawable.class).getDeclaredFields();
Alecs
  • 2,900
  • 1
  • 22
  • 25
1

The OP wanted drawables and I needed layouts. This is what I came up with for layouts. The name.startsWith business lets me ignore system generated layouts, so you may need to tweak that a bit. This should work for any resource type by modifying the value of clz.

public static Map<String,Integer> loadLayouts(){
    final Class<?> clz = R.layout.class;
    Map<String,Integer> layouts = new HashMap<>();
    final Field[] fields = clz.getDeclaredFields();
    for (Field field : fields) {
        String name = field.getName();
        if (
                !name.startsWith("abc_")
                && !name.startsWith("design_")
                && !name.startsWith("notification_")
                && !name.startsWith("select_dialog_")
                && !name.startsWith("support_")
        ) {
            try {
                layouts.put(field.getName(), field.getInt(clz));
            } catch (Exception e) {
                continue;
            }
        }
    }
    return layouts;
}
JohnnyLambada
  • 12,700
  • 11
  • 57
  • 61
0

USE THIS MY CODE

R.drawable drawableResources = new R.drawable();
Class<R.drawable> c = R.drawable.class;
Field[] fields = c.getDeclaredFields();

for (int i = 0, max = fields.length; i < max; i++) {
    final int resourceId;
    try {
        resourceId = fields[i].getInt(drawableResources);
        // call save with param of resourceId
        SaveImage(resourceId);
    } catch (Exception e) {
        continue;
    }
}

...

public void SaveImage(int resId){
    if (!CheckExternalStorage()) {
        return;
    }

    Bitmap bmp = BitmapFactory.decodeResource(getResources(), resID);
    try {
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        OutputStream fOut = null;
        File file = new File(path, "image1.png");
        file.createNewFile();
        fOut = new FileOutputStream(file);
        bmp.compress(Bitmap.CompressFormat.PNG, 100, fOut);
        fOut.flush();
        fOut.close();
        MediaStore.Images.Media.insertImage(this.getContentResolver(), file.getAbsolutePath(), file.getName(), file.getName());
        Log.i(LOGTAG, "Image Written to Exterbal Storage");

    } catch (Exception e) {
        Log.e("saveToExternalStorage()", e.getMessage());
    }
}
0

To create an array of drawables in Kotlin:

    val drawablesFields: Array<Field> = drawable::class.java.fields
    val drawables: ArrayList<Drawable> = ArrayList()
    for (field in drawablesFields) {
        context?.let { ContextCompat.getDrawable(it, field.getInt(null)) }?.let {
            drawables.add (
                it
            )
        }
    }