7

So, I have code that's generating ID's for a number of elements using an AtomicInteger that's set by default at Integer.MAX_VALUE and is decremented from there with each view that gets assigned an ID. So the first view with a generated ID would be Integer.MAX_VALUE - 1, the second would be Integer.MAX_VALUE - 2, etc. The problem I'm afraid of is a collision with IDs generated by Android in R.java.

So my question is how can I detect if an ID is already in use and skip it when I'm generating the IDs. I'm only generating at most 30 IDs so this isn't a huge priority nut I'ld like to make this as bug free as possible.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Brandon
  • 4,486
  • 8
  • 33
  • 39

5 Answers5

8

The following code will tell you if the identifier is an id or not.

static final String PACKAGE_ID = "com.your.package.here:id/"
...
...
int id = <your random id here>
String name = getResources().getResourceName(id);
if (name == null || !name.startsWith(PACKAGE_ID)) {
    // id is not an id used by a layout element.
}
Jens
  • 16,853
  • 4
  • 55
  • 52
  • Thanks! This looks promising. I didn't even think of using getResources. I'll try it out. – Brandon Apr 29 '12 at 16:59
  • @Brandon, you should update your question if you have something more to add. Looking at your edit though it'll be even better as an answer to your own question! – Ben Apr 29 '12 at 19:26
  • 7
    `name` will never be `null`. instead, a `Resources.NotFoundException` will be thrown by `getResourceName()` if the identifier is invalid – sfera Feb 01 '14 at 18:12
5

I modified Jens answer from above since, as stated in comments, name will never be null and exception is thrown instead.

private boolean isResourceIdInPackage(String packageName, int resId){
    if(packageName == null || resId == 0){
        return false;
    }

    Resources res = null;
    if(packageName.equals(getPackageName())){
        res = getResources();
    }else{
        try{
            res = getPackageManager().getResourcesForApplication(packageName);
        }catch(PackageManager.NameNotFoundException e){
            Log.w(TAG, packageName + "does not contain " + resId + " ... " + e.getMessage());
        }
    }

    if(res == null){
        return false;
    }

    return isResourceIdInResources(res, resId);
}

private boolean isResourceIdInResources(Resources res, int resId){

    try{            
        getResources().getResourceName(resId);

        //Didn't catch so id is in res
        return true;

    }catch (Resources.NotFoundException e){
        return false;
    }
}
Chris Sprague
  • 3,158
  • 33
  • 24
1

You can use Java Reflection API to access whatever elements are present in an object of R.id Class.

The code is like this:

Class<R.id> c = R.id.class;

R.id object = new R.id();

Field[] fields = c.getDeclaredFields();

// Iterate through whatever fields R.id has
for (Field field : fields)
{
    field.setAccessible(true);

    // I am just printing field name and value, you can place your checks here

    System.out.println("Value of " + field.getName() + " : " + field.get(object));
}
1

You can use View.generateViewId() which requires min API 17.

From sdk

Generate a value suitable for use in setId(int). This value will not collide with ID values generated at build time by aapt for R.id.

Patriotic
  • 2,103
  • 4
  • 26
  • 36
0

Just an idea ... you could use the findViewById (int id) to check if the id is already in use.

Ruediger
  • 271
  • 1
  • 4
  • 2
    that would only work if the context was set to the specific view that the id was located. In other words with more than one view and different ids that would never work as detection. – MikeIsrael Apr 29 '12 at 09:01