Android: En Vue.setID(int id) par programme - comment éviter le conflit d'ID?

j'ajoute des TextViews programmatiquement dans un for-loop et je les ajoute à un ArrayList.

comment utiliser TextView.setId(int id) ? Quel ID Integer dois-je trouver pour ne pas entrer en conflit avec d'autres IDs?

269
demandé sur ZhiXingZhe - WangYuQi 2009-11-11 13:23:11

14 réponses

selon View documentation

l'identificateur n'a pas à être unique dans la hiérarchie de cette vue. L'identificateur doit être un nombre positif.

donc vous pouvez utiliser n'importe quel entier positif que vous aimez, mais dans ce cas il peut y avoir quelques vues avec des id équivalents. Si vous souhaitez rechercher une vue dans la hiérarchie appelant à setTag avec quelques objets clés peut être pratique.

127
répondu Nikolay Ivanov 2009-11-11 11:11:01

Google a finalement réalisé la nécessité de générer des id uniques pour les vues créées par programmation...

à partir du niveau API 17 et au-dessus, vous pouvez appeler

View.generateViewId ()

puis utiliser View.setId (int) .

dans le cas où vous en avez besoin pour des cibles inférieures au niveau 17, Voici sa mise en œuvre interne en vue.vous pouvez utiliser java directement dans votre projet, mettez - le dans votre classe util ou quelque part:

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;
        }
    }
}
Le numéro D'identification

supérieur à 0x00ffff est réservé aux vues statiques définies dans les fichiers xml /res. (Très probablement 0x7f****** de la R.java dans mes projets.)

dans votre code, vous pouvez faire:

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

        myView.setId(Utils.generateViewId());

    } else {

        myView.setId(View.generateViewId());

    }
445
répondu x.y 2014-09-10 15:39:52

vous pouvez définir des ID que vous utiliserez plus tard dans la classe R.id en utilisant un fichier de ressources xml, et laisser Android SDK leur donner des valeurs uniques pendant 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);
109
répondu Aditya_Android 2014-04-05 05:39:42

vous pouvez aussi définir ids.xml dans res/values . Vous pouvez voir un exemple exact dans le code échantillon d'android.

samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
58
répondu yenliangl 2017-03-31 09:08:15

ça marche 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++;  
}
26
répondu dilettante 2012-07-23 18:17:07

depuis API 17, la classe View a une méthode statique generateViewId() qui va

générer une valeur appropriée pour l'utilisation dans setId(int)

22
répondu Diederik 2013-02-20 08:42:25

(c'était un commentaire à la réponse de dilettante mais c'est devenu trop long...hehe)

bien sûr statique n'est pas nécessaire ici. Vous pouvez utiliser SharedPreferences pour enregistrer, au lieu de static. Quoi qu'il en soit, la raison est de sauvegarder le progrès actuel afin que son pas trop lent pour des mises en page 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 une bonne façon de le faire parce que si vous devez reconstruire votre écran à nouveau (dire onCreate est appelé à nouveau), alors vous voulez probablement recommencer depuis le début de toute façon, en éliminant le besoin de statique. Par conséquent, il suffit d'en faire une variable d'instance plutôt que statique.

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

int fID = 0;

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

cette fonction ci-dessus doit être suffisante. Parce que, d'après ce que je peux dire, les identifiants générés par android sont dans les milliards, donc cela va probablement retourner 1 la première fois, et toujours être assez rapide. Parce que, en fait, il ne sera pas en boucle au-delà des identifiants utilisés pour trouver un non utilisé. Cependant, la boucle est si elle trouve un ID utilisé.

cependant, si vous voulez encore le progrès enregistré entre les recréations ultérieures de votre application, et que vous voulez éviter d'utiliser static. 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 IDs avec android: https://stackoverflow.com/a/13241629/693927

MODIFIER/CORRIGER: Viens de réaliser que j'ai totalement raté son coup de la sauvegarde. Je dois avoir été ivres.

10
répondu Pimp Trizkit 2017-05-23 12:26:27

la bibliothèque' Compat 'supporte désormais la méthode generateViewId() pour les niveaux API prior 17.

assurez-vous d'utiliser une version de la Compat bibliothèque 27.1.0+

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

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

alors vous pouvez simplement utiliser le generateViewId() de la classe ViewCompat au lieu de la classe View comme suit:

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

bon codage !

7
répondu Alex Roussiere 2018-07-20 20:18:24

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

alors que View.generateViewId() nécessite un niveau D'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.

de sorte que vous pouvez utiliser ViewIdGenerator.generateViewId() et View.generateViewId() dans le même heure et ne vous inquiétez pas d'obtenir la même carte d'identité

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();
        }

    }
}
6
répondu fantouch 2014-01-08 16:01:19

pour générer dynamiquement une vue ID API 17 utiliser

generateViewId ()

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

3
répondu Arun C 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
répondu Dmitry 2014-04-25 12:11:41

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'id unique dans la première tentative.

1
répondu Bjørn Stenfeldt 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
répondu chinwo 2016-12-12 20:46:04

Mon Choix:

// Method that could us an unique id

    int getUniqueId(){
        return (int)    
                SystemClock.currentThreadTimeMillis();    
    }
-2
répondu nimi0112 2018-02-22 06:48:20