Respuestas:
De acuerdo con la View
documentación
El identificador no tiene que ser único en la jerarquía de esta vista. El identificador debe ser un número positivo.
Por lo tanto, puede usar cualquier número 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 setTag
con algunos objetos clave puede ser útil.
findViewById
, devolverá la primera que encuentre.
setContentView()
tiene, digamos, 10 vistas con su ID establecido en el mismo número de identificación en la misma jerarquía , entonces una llamada a findViewById([repeated_id])
devolvería la primera vista establecida con esa identificación repetida. A eso me refería.
Desde el nivel API 17 y superior, puede llamar a: View.generateViewId ()
Luego use View.setId (int) .
Si su aplicación está dirigida a un nivel inferior al API 17, use ViewCompat.generateViewId ()
AtomicInteger
implementación de métodos.
for(;;)
lo que nunca había visto antes? ¿Cómo se llama eso?
Puede establecer las ID que usará más adelante en la R.id
clase utilizando un archivo de recursos xml y dejar que el SDK de Android les otorgue 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);
"int currentId = 1000; whateverView.setId(currentId++);
: eso incrementa la ID cada vez que currentId++
se usa, asegurando una ID única, y puedo almacenar el ID en mi ArrayList para acceso posterior.
<resources>
.
También puedes definirlo ids.xml
en res/values
. Puede ver un ejemplo exacto en el código de muestra de Android.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Desde API 17, la View
clase tiene un método estático generateViewId()
que
generar un valor adecuado para usar en setId (int)
Esto funciona para mi:
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++;
}
findViewById()
Es una operación lenta. El enfoque funciona, pero a costa del rendimiento.
(Este fue un comentario a la respuesta del diletante pero se hizo demasiado largo ... jeje)
Por supuesto, aquí no se necesita una estática. 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 usarse una vez, será bastante rápido más tarde. Sin embargo, no creo que esta sea una buena manera de hacerlo porque si tiene que reconstruir su pantalla nuevamente (por ejemplo, onCreate
se le llama nuevamente), entonces de todos modos probablemente quiera comenzar de nuevo, eliminando la necesidad de estática. Por lo tanto, simplemente conviértalo en 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, las ID generadas por Android están en miles de millones, por lo que probablemente esto regrese 1
la primera vez y siempre sea bastante rápido. Porque, en realidad, no estará pasando los ID utilizados para encontrar uno no utilizado. Sin embargo, el ciclo es allí si realmente encuentra una identificación usada.
Sin embargo, si aún desea que se guarde el progreso entre 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 decirle todo lo que necesita saber sobre ID con Android: https://stackoverflow.com/a/13241629/693927
EDITAR / ARREGLO: Acabo de darme cuenta de que me burlé por completo de guardar. Debo haber estado borracho.
La biblioteca 'Compat' ahora también admite el generateViewId()
método para niveles de API anteriores al 17.
Solo asegúrese de usar una versión de la Compat
biblioteca que sea27.1.0+
Por ejemplo, en su build.gradle
archivo, ponga:
implementation 'com.android.support:appcompat-v7:27.1.1
Entonces puede simplemente usar el generateViewId()
de la ViewCompat
clase en lugar de la View
clase de la siguiente manera:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
¡Feliz codificación!
Solo una adición a la respuesta de @phantomlimb,
mientras que View.generateViewId()
requiere Nivel API> = 17,
esta herramienta es compatible con todas las API
según el nivel de API actual,
decide el clima usando la API del sistema o no.
para que pueda usar ViewIdGenerator.generateViewId()
y View.generateViewId()
al mismo tiempo y no se preocupe por obtener la misma identificación
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 fantouchx@gmail.com
*/
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();
}
}
}
for (;;) { … }
proviene del código fuente de Android.
generateViewId()
else { return View.generateViewId(); }
¿Esto irá a bucle infinito para un nivel de API menor a 17 dispositivos?
Para generar dinámicamente el formulario de Id. De vista API 17, use
Lo que generará un valor adecuado para su uso en setId(int)
. Este valor no colisionará con los valores de ID generados en tiempo de compilación por aapt for R.id
.
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();
}
}
}
Yo suelo:
public synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
Al usar un número aleatorio siempre tengo una gran posibilidad de obtener la identificación única en el primer intento.
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);
}
findViewById
alguna garantía sobre qué vista se devuelve si hay más de una con la misma ID? Los documentos no mencionan nada.