How can I assign an ID to a view programmatically?

150,373

Solution 1

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 Views 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 ids 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 ids

  • XML-assigned ids will be unique.
  • Code-assigned ids do not have to be unique
  • Code-assigned ids can (theoretically) conflict with XML-assigned ids.
  • These conflicting ids won't matter if queried correctly (keep reading).

When (and why) conflicting ids 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 ids 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 IDs

  • 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 Views.
  • If you need or want, assign any ids 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 TextViews 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

Solution 2

You can just use the View.setId(integer) for this. In the XML, even though you're setting a String id, this gets converted into an integer. Due to this, you can use any (positive) Integer for the Views you add programmatically.

According to View documentation

The identifier does not have to be unique in this view's hierarchy. The identifier should be a positive number.

So you can use any positive integer you like, but in this case there can be some views with equivalent id's. If you want to search for some view in hierarchy calling to setTag with some key objects may be handy.

Credits to this answer.

Solution 3

Yes, you can call setId(value) in any view with any (positive) integer value that you like and then find it in the parent container using findViewById(value). Note that it is valid to call setId() with the same value for different sibling views, but findViewById() will return only the first one.

Share:
150,373

Related videos on Youtube

DuyguK
Author by

DuyguK

Updated on September 30, 2020

Comments

  • DuyguK
    DuyguK over 3 years

    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?

  • Peter Ajtai
    Peter Ajtai over 12 years
    Note that you have to use an integer greater than zero.
  • Aniket Thakur
    Aniket Thakur over 10 years
    though findViewById(int) will iterate recursively through the view hierarchy from the View you specify and return the first View it finds with a matching id specified in 1st answer is most accurate.
  • AllDayAmazing
    AllDayAmazing over 10 years
    In addition to this, it might be useful for someone in coding similiar solutions to be aware of View.generateViewId() in API > 17 for non-conflicting IDs
  • Karu
    Karu about 10 years
    Note that findViewById does a depth-first exploration, so "As long as there are no code-assigned ids assigned above an XML-defined id in the hierarchy" isn't technically correct; it's "before" rather than "above".
  • Karu
    Karu about 10 years
    Yeah, calling findViewById on a known ancestor is a good idea for performance reasons, but it doesn't guarantee it will find an immediate child if there is one with the correct ID.
  • ThomasW
    ThomasW about 7 years
    In more recent versions of the Android developer tools, programmatically setting the ID to an arbitrary value will be flagged as a compiler error. The value is expected to be an actual resource ID.
  • CodeShane
    CodeShane about 7 years
    I believe this was even the case when I answered this five years ago - that's why the custom id's had to be defined in ids.xml . For truly arbitrary ID's, use View.generateViewId() (API 17). (Please clarify your point if I've missed it.)
  • UrK
    UrK almost 5 years
    > (Also, an id assigned to a Preference in XML is not used when the Preference generates its View.) Very interested about that. My code inherits from PreferenceDialogFragmentCompat it seems like ids from R.id do not match the ones view hierarchy. This way I cannot find the view by ID.