How can I assign an ID to a view programmatically?

android, android-identifiers, android-resources, android-view, findviewbyid

Solution

Android `id` overview

An Android `id` is an integer commonly used to identify views; this `id` can be assigned via XML (when possible) and via code (programmatically.) The `id` is most useful for getting references for XML-defined `View`s generated by an `Inflater` (such as by using `setContentView`.)

Assign `id` via `XML`

- Add an attribute of `android:id="@+id/`somename`"` to your view.

- When your application is built, the `android:id` will be assigned a unique `int` for use in code.

- Reference your `android:id`'s `int` value in code using "`R.id.`somename" (effectively a constant.)

- this `int` can change from build to build so never copy an id from `gen/`package.name/`R.java`, just use "`R.id.`somename".

- (Also, an `id` assigned to a `Preference` in XML is not used when the `Preference` generates its `View`.)

Assign `id` via code (programmatically)

- Manually set `id`s using `someView.setId(`int`);`

- The `int` must be positive, but is otherwise arbitrary- it can be whatever you want (keep reading if this is frightful.)

- For example, if creating and numbering several views representing items, you could use their item number.

Uniqueness of `id`s

- `XML`-assigned `id`s will be unique.

- Code-assigned `id`s do not have to be unique

- Code-assigned `id`s can (theoretically) conflict with `XML`-assigned `id`s.

- These conflicting `id`s won't matter if queried correctly (keep reading).

When (and why) conflicting `id`s don't matter

- `findViewById(int)` will iterate depth-first recursively through the view hierarchy from the View you specify and return the first `View` it finds with a matching `id`.

- As long as there are no code-assigned `id`s assigned before an XML-defined `id` in the hierarchy, `findViewById(R.id.somename)` will always return the XML-defined View so `id`'d.

Dynamically Creating Views and Assigning `ID`s

- In layout XML, define an empty `ViewGroup` with `id`.

- Such as a `LinearLayout` with `android:id="@+id/placeholder"`.

- Use code to populate the placeholder `ViewGroup` with `View`s.

- If you need or want, assign any `id`s that are convenient to each view.

Query these child views using placeholder.findViewById(convenientInt);

API 17 introduced `View.generateViewId()` which allows you to generate a unique ID.

If you choose to keep references to your views around, be sure to instantiate them with `getApplicationContext()` and be sure to set each reference to null in `onDestroy`. Apparently leaking the `Activity` (hanging onto it after is is destroyed) is wasteful.. :)

Reserve an XML `android:id` for use in code

API 17 introduced `View.generateViewId()` which generates a unique ID. (Thanks to take-chances-make-changes for pointing this out.)*

If your `ViewGroup` cannot be defined via XML (or you don't want it to be) you can reserve the id via XML to ensure it remains unique:

Here, values/ids.xml defines a custom `id`:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="reservedNamedId" type="id"/>
</resources>

Then once the ViewGroup or View has been created, you can attach the custom id

myViewGroup.setId(R.id.reservedNamedId);

Conflicting `id` example

For clarity by way of obfuscating example, lets examine what happens when there is an `id` conflict behind the scenes.

layout/mylayout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <LinearLayout
        android:id="@+id/placeholder"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
</LinearLayout>

To simulate a conflict, lets say our latest build assigned `R.id.placeholder`(`@+id/placeholder`) an `int` value of `12`..

Next, MyActivity.java defines some adds views programmatically (via code):

int placeholderId = R.id.placeholder; // placeholderId==12
// returns *placeholder* which has id==12:
ViewGroup placeholder = (ViewGroup)this.findViewById(placeholderId);
for (int i=0; i<20; i++){
    TextView tv = new TextView(this.getApplicationContext());
    // One new TextView will also be assigned an id==12:
    tv.setId(i);
    placeholder.addView(tv);
}

So `placeholder` and one of our new `TextView`s both have an `id` of 12! But this isn't really a problem if we query placeholder's child views:

// Will return a generated TextView:
 placeholder.findViewById(12);

// Whereas this will return the ViewGroup *placeholder*;
// as long as its R.id remains 12: 
Activity.this.findViewById(12);

*Not so bad

Problem

In an XML file, we can assign an ID to a view like `android:id="@+id/something"` and then call `findViewById()`, but when creating a view programmatically, how do I assign an ID? I think `setId()` is not the same as default assignment. `setId()` is extra. Can anybody correct me?

Original source

Related problems