Reputation: 83269
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?
Upvotes: 26
Views: 33278
Reputation: 3748
A possible Kotlin + Jetpack Compose solution would look like this. Note that this one also filters only the drawable that contain the name "icon" but one can also just leave away the filter:
val iconResourceIds = R.drawable::class.java.fields.filter { it.name.contains("icon") }.map {
LocalContext.current.resources.getIdentifier(it.name, "drawable", LocalContext.current.packageName)
}
One can then draw all icons with their resource names and DP sizes like this:
LazyVerticalGrid(
modifier = Modifier.background(Color.White),
columns = GridCells.Adaptive(minSize = 100.dp),
contentPadding = PaddingValues(18.dp),
verticalArrangement = Arrangement.spacedBy(32.dp),
horizontalArrangement = Arrangement.spacedBy(18.dp),
) {
items(iconResourceIds) { iconResourceId ->
Column(horizontalAlignment = Alignment.CenterHorizontally) {
val painter = painterResource(id = iconResourceId)
Icon(
painter = painter,
contentDescription = null,
tint = Color.Black
)
val name = LocalContext.current.resources.getResourceEntryName(iconResourceId)
Text(
text = name,
color = Color.Black,
textAlign = TextAlign.Center,
)
val widthDp = with(LocalDensity.current) { painter.intrinsicSize.width.toDp() }.value.roundToInt().toString()
val heightDp = with(LocalDensity.current) { painter.intrinsicSize.height.toDp() }.value.roundToInt().toString()
Text(
text = "$widthDp x $heightDp",
color = Color.Black,
textAlign = TextAlign.Center,
)
}
}
}
Upvotes: 1
Reputation: 940
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
)
}
}
Upvotes: 0
Reputation:
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());
}
}
Upvotes: 0
Reputation: 1780
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
}
Upvotes: 7
Reputation: 12826
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;
}
Upvotes: 1
Reputation: 2920
Just do this:
Field[] declaredFields = (R.drawable.class).getDeclaredFields();
Upvotes: 2
Reputation: 10681
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.
Upvotes: 6
Reputation: 14698
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);
Upvotes: 9
Reputation: 6212
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();
}
Upvotes: 8
Reputation: 28932
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.
Upvotes: 4
Reputation: 93133
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);
Upvotes: 3
Reputation: 83269
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!
Upvotes: 35