Risposte:
Secondo la View
documentazione
L'identificatore non deve essere univoco nella gerarchia di questa vista. L'identificatore dovrebbe essere un numero positivo.
Quindi puoi usare qualsiasi numero intero positivo che ti piace, ma in questo caso possono esserci alcune viste con ID equivalenti. Se vuoi cercare una vista nella gerarchia setTag
, può essere utile chiamare con alcuni oggetti chiave.
findViewById
verrà restituito il primo trovato.
setContentView()
ha, diciamo, 10 viste con il loro ID impostato sullo stesso numero ID nella stessa gerarchia , quindi una chiamata a findViewById([repeated_id])
restituirebbe la prima vista impostata con quell'ID ripetuto. Ecco cosa intendevo.
Dal livello API 17 in poi, puoi chiamare: View.generateViewId ()
Quindi utilizzare View.setId (int) .
Se la tua app ha un target inferiore al livello API 17, usa ViewCompat.generateViewId ()
AtomicInteger
all'implementazione dei metodi.
for(;;)
non ho mai visto prima. Come si chiama?
Puoi impostare gli ID che utilizzerai più avanti in R.id
classe utilizzando un file di risorse xml e lasciare che Android SDK fornisca loro valori univoci durante la compilazione.
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"/>
Per usarlo nel codice:
myEditTextView.setId(R.id.my_edit_text_1);
"int currentId = 1000; whateverView.setId(currentId++);
- Ciò incrementa l'ID ogni volta che currentId++
viene utilizzato, garantendo un ID univoco e posso archiviare il ID nella mia ArrayList per un accesso successivo.
<resources>
.
Inoltre è possibile definire ids.xml
in res/values
. Puoi vedere un esempio esatto nel codice di esempio di Android.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Dall'API 17, la View
classe ha un metodo statico generateViewId()
che lo farà
generare un valore adatto per l'uso in setId (int)
Questo funziona per me:
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()
è un'operazione lenta. L'approccio funziona, ma a scapito delle prestazioni.
(Questo è stato un commento alla risposta del dilettante, ma è passato troppo tempo ... hehe)
Ovviamente qui non è necessaria una statica. È possibile utilizzare SharedPreferences per salvare, anziché statico. In entrambi i casi, il motivo è quello di salvare i progressi attuali in modo che non sia troppo lento per layout complicati. Perché, in effetti, dopo essere stato usato una volta, sarà piuttosto veloce in seguito. Tuttavia, non credo che questo sia un buon modo per farlo perché se devi ricostruire nuovamente il tuo schermo (diciamo che onCreate
viene chiamato di nuovo), allora probabilmente vorrai ricominciare dall'inizio dall'inizio, eliminando la necessità di elettricità statica. Pertanto, basta renderlo una variabile di istanza anziché statica.
Ecco una versione più piccola che funziona un po 'più veloce e potrebbe essere più facile da leggere:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
Questa funzione sopra dovrebbe essere sufficiente. Perché, per quanto posso dire, gli ID generati da Android sono tra i miliardi, quindi probabilmente tornerà 1
la prima volta e sarà sempre abbastanza veloce. Perché, in realtà non passerà attraverso gli ID usati per trovarne uno inutilizzato. Tuttavia, il ciclo è lì se dovesse effettivamente trovare un ID usato.
Tuttavia, se desideri comunque salvare i progressi tra le successive ricreazioni della tua app e vuoi evitare l'uso di elettricità statica. Ecco la versione di 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;
}
Questa risposta a una domanda simile dovrebbe dirti tutto ciò che devi sapere sugli ID con Android: https://stackoverflow.com/a/13241629/693927
EDIT / FIX: ho appena realizzato che ho salvato parecchio il salvataggio. Devo essere stato ubriaco.
La libreria "Compat" ora supporta anche il generateViewId()
metodo per i livelli API precedenti al 17.
Assicurati di usare una versione della Compat
libreria che è27.1.0+
Ad esempio, nel tuo build.gradle
file, inserisci:
implementation 'com.android.support:appcompat-v7:27.1.1
Quindi puoi semplicemente usare il generateViewId()
dalla ViewCompat
classe invece della View
classe come segue:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
Buona programmazione!
Solo un'aggiunta alla risposta di @phantomlimb,
mentre View.generateViewId()
richiede Livello API> = 17,
questo strumento è compatibile con tutte le API.
in base all'attuale livello API,
decide se utilizzare o meno l'API di sistema.
così puoi usare ViewIdGenerator.generateViewId()
e View.generateViewId()
allo stesso tempo e non preoccuparti di ottenere lo stesso 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 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 dal codice sorgente di Android.
generateViewId()
else { return View.generateViewId(); }
questo andrà in loop infinito per un livello API inferiore a 17 dispositivi?
Al fine di generare dinamicamente l'utilizzo dell'ID di visualizzazione ID modulo 17
Che genererà un valore adatto per l'uso in setId(int)
. Questo valore non si scontrerà con i valori ID generati al momento della creazione da 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();
}
}
}
Io uso:
public synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
Usando un numero casuale ho sempre un'enorme possibilità di ottenere l'ID univoco al primo tentativo.
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
garanzie su quale vista viene restituita se ce n'è più di una con lo stesso ID? I documenti non menzionano nulla.