Android: Vista.setId (int id) programáticamente - ¿cómo evitar conflictos de ID?


Estoy agregando TextViews programáticamente en un bucle for y agregándolas a una ArrayList.

¿Cómo uso TextView.setId(int id)? ¿Qué ID de entero se me ocurre para que no entre en conflicto con otros ID?

Author: ZhiXingZhe - WangYuQi, 2009-11-11

14 answers

Según la documentación View

El identificador no tiene que ser único en la jerarquía de esta vista. El identificador debe ser un número positivo.

Así que puede usar cualquier entero positivo que desee, pero en este caso puede haber algunas vistas con ID equivalentes. Si desea buscar alguna vista en la jerarquía, llamar a setTag con algunos objetos clave puede ser útil.

 127
Author: Nikolay Ivanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-11 11:11:01

Google finalmente se dio cuenta de la necesidad de generar ID únicos para vistas creadas mediante programación...

Desde el nivel de API 17 y superior, puede llamar a

Vista.generateViewId()

Luego use la vista .setId (int).

En caso de que lo necesite para objetivos inferiores al nivel 17, aquí está su implementación interna a la vista.java se puede utilizar directamente en su proyecto, ponerlo en su clase util o en algún lugar:

private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);

/**
 * Generate a value suitable for use in {@link #setId(int)}.
 * This value will not collide with ID values generated at build time by aapt for R.id.
 *
 * @return a generated ID value
 */
public static int generateViewId() {
    for (;;) {
        final int result = sNextGeneratedId.get();
        // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
        int newValue = result + 1;
        if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
        if (sNextGeneratedId.compareAndSet(result, newValue)) {
            return result;
        }
    }
}

ID número mayor que 0x00FFFFFF es reservado para vistas estáticas definidas en los archivos xml / res. (Lo más probable 0x7f****** de la R.java en mis proyectos.)

En tu código, puedes hacer:

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {

        myView.setId(Utils.generateViewId());

    } else {

        myView.setId(View.generateViewId());

    }
 445
Author: x.y,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-10 15:39:52

Puedes establecer IDS que usarás más adelante en la clase R.id usando un archivo de recursos xml, y dejar que Android SDK les dé valores únicos durante el tiempo de compilación.

 res/values/ids.xml

<item name="my_edit_text_1" type="id"/>
<item name="my_button_1" type="id"/>
<item name="my_time_picker_1" type="id"/>

Para usarlo en el código:

myEditTextView.setId(R.id.my_edit_text_1);
 109
Author: Aditya_Android,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-05 05:39:42

También puedes definir ids.xml en res/values. Puedes ver un ejemplo exacto en el código de ejemplo de Android.

samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
 58
Author: yenliangl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-31 09:08:15

Esto funciona para mí:

static int id = 1;

// Returns a valid id that isn't in use
public int findId(){  
    View v = findViewById(id);  
    while (v != null){  
        v = findViewById(++id);  
    }  
    return id++;  
}
 26
Author: dilettante,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-23 18:17:07

Desde API 17, la clase View tiene un método estático generateViewId() que será

Generar un valor adecuado para su uso en setId(int)

 22
Author: Diederik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-20 08:42:25

(Este fue un comentario a la respuesta de dilettante, pero se hizo demasiado largo...jeje)

Por supuesto que una estática no es necesaria aquí. Puede usar SharedPreferences para guardar, en lugar de static. De cualquier manera, la razón es guardar el progreso actual para que no sea demasiado lento para diseños complicados. Porque, de hecho, después de su utilizado una vez, será bastante rápido más tarde. Sin embargo, no creo que esta sea una buena manera de hacerlo porque si tienes que reconstruir tu pantalla de nuevo (por ejemplo, onCreate recibe una llamada de nuevo), entonces probablemente quieras empezar de nuevo desde el principio de todos modos, eliminando la necesidad de estática. Por lo tanto, solo haz que sea una variable de instancia en lugar de estática.

Aquí hay una versión más pequeña que se ejecuta un poco más rápido y podría ser más fácil de leer:

int fID = 0;

public int findUnusedId() {
    while( findViewById(++fID) != null );
    return fID;
}

Esta función anterior debería ser suficiente. Porque, por lo que puedo decir, los ID generados por Android están en miles de millones, por lo que probablemente volverá 1 la primera vez y siempre será bastante rápido. Porque, en realidad no será un bucle pasado los identificadores utilizados para encontrar uno no utilizado. Sin embargo, el bucle es allí debería encontrar un ID usado.

Sin embargo, si aún desea que el progreso se guarde entre las recreaciones posteriores de su aplicación y desea evitar el uso de estática. Aquí está la versión de SharedPreferences:

SharedPreferences sp = getSharedPreferences("your_pref_name", MODE_PRIVATE);

public int findUnusedId() {
    int fID = sp.getInt("find_unused_id", 0);
    while( findViewById(++fID) != null );
    SharedPreferences.Editor spe = sp.edit();
    spe.putInt("find_unused_id", fID);
    spe.commit();
    return fID;
}

Esta respuesta a una pregunta similar debería decirte todo lo que necesitas saber sobre IDs con android: https://stackoverflow.com/a/13241629/693927

EDITAR / ARREGLAR: Acabo de darme cuenta la salvé totalmente mal. Debo haber estado borracho.

 10
Author: Pimp Trizkit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:27

La biblioteca 'Compat' ahora también admite el método generateViewId() para los niveles de API anteriores al 17.

Solo asegúrese de usar una versión de la biblioteca Compat que sea 27.1.0+

Por ejemplo, en su archivo build.gradle, ponga:

implementation 'com.android.support:appcompat-v7:27.1.1

Entonces puedes simplemente usar el generateViewId() de la clase ViewCompat en lugar de la clase View de la siguiente manera:

//Will assign a unique ID myView.id = ViewCompat.generateViewId()

Feliz codificación !

 7
Author: Alex Roussiere,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-20 20:18:24

Solo una adición a la respuesta de @phantomlimb,

Mientras que View.generateViewId() requieren nivel de API > = 17,
esta herramienta es compatibe con todas las API.

De acuerdo con el nivel de API actual,
decide el tiempo usando la API del sistema o no.

Así que puedes usar ViewIdGenerator.generateViewId() y View.generateViewId() en el al mismo tiempo y no se preocupe por obtener el mismo id

import java.util.concurrent.atomic.AtomicInteger;

import android.annotation.SuppressLint;
import android.os.Build;
import android.view.View;

/**
 * {@link View#generateViewId()}要求API Level >= 17,而本工具类可兼容所有API Level
 * <p>
 * 自动判断当前API Level,并优先调用{@link View#generateViewId()},即使本工具类与{@link View#generateViewId()}
 * 混用,也能保证生成的Id唯一
 * <p>
 * =============
 * <p>
 * while {@link View#generateViewId()} require API Level >= 17, this tool is compatibe with all API.
 * <p>
 * according to current API Level, it decide weather using system API or not.<br>
 * so you can use {@link ViewIdGenerator#generateViewId()} and {@link View#generateViewId()} in the
 * same time and don't worry about getting same id
 * 
 * @author [email protected]
 */
public class ViewIdGenerator {
    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);

    @SuppressLint("NewApi")
    public static int generateViewId() {

        if (Build.VERSION.SDK_INT < 17) {
            for (;;) {
                final int result = sNextGeneratedId.get();
                // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
                int newValue = result + 1;
                if (newValue > 0x00FFFFFF)
                    newValue = 1; // Roll over to 1, not 0.
                if (sNextGeneratedId.compareAndSet(result, newValue)) {
                    return result;
                }
            }
        } else {
            return View.generateViewId();
        }

    }
}
 6
Author: fantouch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-08 16:01:19

Para generar dinámicamente la API de formulario de identificación de vista 17 use

GenerateViewId()

Que generará un valor adecuado para su uso en setId(int). Este valor no chocará con los valores ID generados en el tiempo de compilación por aapt para R.id.

 3
Author: Arun C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-13 11:52:26
int fID;
do {
    fID = Tools.generateViewId();
} while (findViewById(fID) != null);
view.setId(fID);

...

public class Tools {
    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
    public static int generateViewId() {
        if (Build.VERSION.SDK_INT < 17) {
            for (;;) {
                final int result = sNextGeneratedId.get();
                int newValue = result + 1;
                if (newValue > 0x00FFFFFF)
                    newValue = 1; // Roll over to 1, not 0.
                if (sNextGeneratedId.compareAndSet(result, newValue)) {
                    return result;
                }
            }
        } else {
            return View.generateViewId();
        }
    }
}
 2
Author: Dmitry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-25 12:11:41

Utilizo:

public synchronized int generateViewId() {
    Random rand = new Random();
    int id;
    while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
    return id;
}

Usando un número aleatorio siempre tengo una gran oportunidad de obtener el id único en el primer intento.

 1
Author: Bjørn Stenfeldt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-19 22:38:44
public String TAG() {
    return this.getClass().getSimpleName();
}

private AtomicInteger lastFldId = null;

public int generateViewId(){

    if(lastFldId == null) {
        int maxFld = 0;
        String fldName = "";
        Field[] flds = R.id.class.getDeclaredFields();
        R.id inst = new R.id();

        for (int i = 0; i < flds.length; i++) {
            Field fld = flds[i];

            try {
                int value = fld.getInt(inst);

                if (value > maxFld) {
                    maxFld = value;
                    fldName = fld.getName();
                }
            } catch (IllegalAccessException e) {
                Log.e(TAG(), "error getting value for \'"+ fld.getName() + "\' " + e.toString());
            }
        }
        Log.d(TAG(), "maxId="+maxFld +"  name="+fldName);
        lastFldId = new AtomicInteger(maxFld);
    }

    return lastFldId.addAndGet(1);
}
 0
Author: chinwo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-12 20:46:04

Mi elección:

// Method that could us an unique id

    int getUniqueId(){
        return (int)    
                SystemClock.currentThreadTimeMillis();    
    }
 -2
Author: nimi0112,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-22 06:48:20