Réponses:
Selon la View
documentation
L'identifiant n'a pas besoin d'être unique dans la hiérarchie de cette vue. L'identifiant doit être un nombre positif.
Vous pouvez donc utiliser n'importe quel entier positif que vous aimez, mais dans ce cas, il peut y avoir des vues avec des identifiants équivalents. Si vous souhaitez rechercher une vue dans la hiérarchie, appeler setTag
avec certains objets clés peut être pratique.
findViewById
, le premier sera trouvé.
setContentView()
a, disons, 10 vues avec leur identifiant défini sur le même numéro d'identification dans la même hiérarchie , alors un appel à findViewById([repeated_id])
retournerait le premier ensemble de vues avec cet identifiant répété. C'est ce que je voulais dire.
Depuis l'API de niveau 17 et supérieur, vous pouvez appeler: View.generateViewId ()
Utilisez ensuite View.setId (int) .
Si votre application est ciblée à un niveau inférieur au niveau 17 de l'API, utilisez ViewCompat.generateViewId ()
AtomicInteger
implémentation des méthodes.
for(;;)
je n'ai jamais vu ça auparavant. Comment ça s'appelle?
Vous pouvez définir les identifiants que vous utiliserez plus tard en R.id
classe à l'aide d'un fichier de ressources xml et laisser le SDK Android leur donner des valeurs uniques lors de la compilation.
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"/>
Pour l'utiliser dans le code:
myEditTextView.setId(R.id.my_edit_text_1);
"int currentId = 1000; whateverView.setId(currentId++);
- Cela incrémente l'ID à chaque currentId++
utilisation, garantissant un ID unique, et je peux stocker le ID dans ma liste de tableaux pour un accès ultérieur.
<resources>
.
Vous pouvez également définir ids.xml
dans res/values
. Vous pouvez voir un exemple exact dans l'exemple de code d'Android.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Depuis l'API 17, la View
classe a une méthode statique generateViewId()
qui
générer une valeur adaptée à une utilisation dans setId (int)
Cela fonctionne pour moi:
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()
est une opération lente. L'approche fonctionne, mais au détriment des performances.
(C'était un commentaire à la réponse de dilettante mais ça devenait trop long ... hehe)
Bien sûr, une statique n'est pas nécessaire ici. Vous pouvez utiliser SharedPreferences pour enregistrer, au lieu de statique. Quoi qu'il en soit, la raison est de sauvegarder la progression actuelle afin qu'elle ne soit pas trop lente pour les mises en page compliquées. Car, en fait, après son utilisation une fois, ce sera plutôt rapide plus tard. Cependant, je ne pense pas que ce soit une bonne façon de le faire, car si vous devez reconstruire votre écran (par exemple, il onCreate
est rappelé), vous voudrez probablement recommencer depuis le début de toute façon, éliminant le besoin de statique. Par conséquent, faites-en simplement une variable d'instance au lieu de statique.
Voici une version plus petite qui s'exécute un peu plus rapidement et pourrait être plus facile à lire:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
Cette fonction ci-dessus devrait être suffisante. Parce que, pour autant que je sache, les identifiants générés par Android sont des milliards, donc cela reviendra probablement 1
la première fois et sera toujours assez rapide. Parce que, en fait, il ne dépassera pas les ID utilisés pour en trouver un inutilisé. Cependant, la boucle est là si elle trouve réellement un ID utilisé.
Cependant, si vous souhaitez toujours enregistrer la progression entre les recréations suivantes de votre application et éviter d'utiliser de l'électricité statique. Voici la version 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;
}
Cette réponse à une question similaire devrait vous dire tout ce que vous devez savoir sur les identifiants avec Android: https://stackoverflow.com/a/13241629/693927
EDIT / FIX: Je viens de réaliser que j'ai complètement raté la sauvegarde. Je devais être ivre.
La bibliothèque 'Compat' prend désormais également en charge la generateViewId()
méthode pour les niveaux d'API antérieurs à 17.
Assurez-vous simplement d'utiliser une version de la Compat
bibliothèque qui est27.1.0+
Par exemple, dans votre build.gradle
fichier, mettez:
implementation 'com.android.support:appcompat-v7:27.1.1
Ensuite, vous pouvez simplement utiliser le generateViewId()
de la ViewCompat
classe au lieu de la View
classe comme suit:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
Bon codage!
Juste un ajout à la réponse de @phantomlimb,
bien que View.generateViewId()
nécessite un niveau API> = 17,
cet outil est compatible avec toutes les API.
selon le niveau d'API actuel,
il décide de la météo en utilisant ou non l'API du système.
afin que vous puissiez utiliser ViewIdGenerator.generateViewId()
et View.generateViewId()
en même temps et ne vous inquiétez pas d'obtenir le même identifiant
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 (;;) { … }
provient du code source Android.
generateViewId()
else { return View.generateViewId(); }
cela ira en boucle infinie pour un niveau api inférieur à 17 appareils?
Afin de générer dynamiquement le formulaire ID de vue, utilisez l'API 17
Ce qui générera une valeur appropriée pour une utilisation dans setId(int)
. Cette valeur n'entrera pas en collision avec les valeurs d'ID générées au moment de la construction par aapt pour 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();
}
}
}
J'utilise:
public synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
En utilisant un nombre aléatoire, j'ai toujours une énorme chance d'obtenir l'identifiant unique lors de la première tentative.
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
des garanties quant à la vue qui est retournée s'il y en a plusieurs avec le même ID? Les documents ne mentionnent rien.