web-dev-qa-db-fra.com

Android: View.setID (int id) par programme - comment éviter les conflits d’ID?

J'ajoute des TextViews par programmation dans une boucle et les ajoute à une ArrayList.

Comment utiliser TextView.setId(int id)? Quel ID entier est-ce que je trouve pour qu'il ne soit pas en conflit avec d'autres ID?

297
znq

Selon la documentation View

L'identifiant ne doit pas nécessairement ê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, mais dans ce cas, il peut y avoir des vues avec des identifiants équivalents. Si vous souhaitez rechercher une vue dans la hiérarchie, l'appel de setTag avec certains objets de clé peut s'avérer utile.

136
Nikolay Ivanov

À partir du niveau 17 de l'API, vous pouvez appeler: View.generateViewId ()

Ensuite, utilisez View.setId (int) .

Si votre application est ciblée à un niveau inférieur au niveau 17 de l'API, utilisez ViewCompat.generateViewId ()

518
xy uber.com

Vous pouvez définir les identifiants que vous utiliserez plus tard dans la classe R.id à l'aide d'un fichier de ressources xml, puis laissez Android SDK leur attribuer 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);
123
Aditya_Android

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
59
yenliangl

Depuis l’API 17, la classe View a un méthode statiquegenerateViewId() qui

générer une valeur utilisable dans setId (int)

27
Diederik

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++;  
}
25
dilettante

(C'était un commentaire à la réponse de dilettante mais c'était 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 en est d’enregistrer la progression en cours pour qu’elle ne soit pas trop lente pour les présentations compliquées. Parce que, en fait, après son utilisation une fois, il sera plutôt rapide plus tard. Cependant, je ne pense pas que ce soit un bon moyen de le faire car si vous devez reconstruire votre écran à nouveau (par exemple, onCreate est appelé de nouveau), vous voudrez probablement recommencer depuis le début, éliminant ainsi la nécessité de statique. Par conséquent, il suffit d’en faire une variable d’instance plutôt que statique.

Voici une version plus petite qui fonctionne un peu plus vite et qui pourrait être plus facile à lire:

int fID = 0;

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

Cette fonction ci-dessus devrait suffire. Parce que, autant que je sache, les identifiants générés par Android se chiffrent en milliards, le message retournera donc probablement 1 la première fois et sera toujours assez rapide. En effet, il ne passera pas réellement devant les identifiants utilisés pour en trouver un non utilisé. Cependant, la boucle est il convient de trouver un identifiant utilisé.

Toutefois, si vous souhaitez toujours enregistrer la progression entre les reproductions suivantes de votre application et que vous souhaitez éviter d'utiliser des données statiques. 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 sous Android: https://stackoverflow.com/a/13241629/693927

EDIT/FIX: Je viens de me rendre compte que j'ai totalement gaffé la sauvegarde. Je devais être saoul.

10
Pimp Trizkit

La bibliothèque 'Compat' prend désormais également en charge la méthode generateViewId() pour les niveaux d'API antérieurs à 17 ans.

Assurez-vous simplement d'utiliser une version de la bibliothèque Compat qui est 27.1.0+

Par exemple, dans votre fichier build.gradle, mettez:

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

Ensuite, vous pouvez simplement utiliser la generateViewId() de la classe ViewCompat à la place de la classe View comme suit:

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

Bon codage!

8
Alex Roussiere

Juste un ajout à la réponse de @phantomlimb,

tandis que View.generateViewId() nécessite un niveau API> = 17,
Cet outil est compatible avec toutes les API.

selon le niveau actuel de l'API,
il décide de la météo en utilisant l’API du système ou non.

afin que vous puissiez utiliser ViewIdGenerator.generateViewId() et View.generateViewId() en même temps sans vous soucier 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 [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
fantouch

Afin de générer dynamiquement l’Aide 17 du formulaire View Id, utilisez

generateViewId ()

Ce qui générera une valeur utilisable dans setId(int). Cette valeur n'entre pas en conflit avec les valeurs d'ID générées au moment de la construction par aapt pour R.id.

3
Arun C
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
Dmitry

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 chance énorme d'obtenir l'identifiant unique du premier coup.

1
Bjørn Stenfeldt
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
chinwo