एंड्रॉइड: View.setID (इंट आईडी) प्रोग्रामेटिक रूप से - आईडी संघर्ष से कैसे बचें?


334

मैं टेक्स्ट-लूप को प्रोग्राम-फॉर-लूप में जोड़ रहा हूं और उन्हें एक ArrayList में जोड़ रहा हूं।

मैं कैसे उपयोग TextView.setId(int id)करूं? मैं कौन-सी इंटेगर आईडी लेकर आता हूं, ताकि यह अन्य आईडी के साथ संघर्ष न करे?

जवाबों:


146

Viewप्रलेखन के अनुसार

इस दृश्य के पदानुक्रम में पहचानकर्ता का विशिष्ट होना आवश्यक नहीं है। पहचानकर्ता एक सकारात्मक संख्या होनी चाहिए।

इसलिए आप अपनी पसंद के किसी भी सकारात्मक पूर्णांक का उपयोग कर सकते हैं, लेकिन इस मामले में समतुल्य आईडी के साथ कुछ विचार हो सकते हैं। यदि आप पदानुक्रम में कुछ दृश्य देखने के लिए setTagकुछ प्रमुख वस्तुओं के साथ कॉल करना चाहते हैं तो यह आसान हो सकता है।


2
दिलचस्प है, मुझे इस बात की जानकारी नहीं थी कि आईडी अद्वितीय होने की आवश्यकता नहीं है? तो क्या findViewByIdकोई गारंटी देता है कि अगर एक ही आईडी के साथ एक से अधिक है तो किस दृश्य को लौटाया जाए? डॉक्स कुछ भी उल्लेख नहीं करता है।
माथियास

26
मुझे लगता है कि डॉक्स ने इस बारे में कुछ उल्लेख किया है। यदि आप एक ही पदानुक्रम में एक ही आईडी के साथ विचार करते हैं, तो findViewByIdपहले इसे वापस मिलेगा।
कांडेरा

2
@ अगर मुझे सही से समझ में नहीं आ रहा है कि आपका क्या मतलब है। मैंने जो कहने की कोशिश की वह यह थी कि यदि आपके द्वारा सेट किया गया लेआउट मान setContentView()लें कि उनके आइडी के साथ 10 आइडिया एक ही पदानुक्रम में एक ही आईडी नंबर पर सेट है , तो कॉल findViewById([repeated_id])एक बार दोहराया जाने वाले आईडी के साथ सेट किए गए पहले दृश्य को वापस करेगा। मेरा मतलब यही था।
कनाडे

51
-1 मैं इस जवाब से सहमत नहीं हूं क्योंकि onSaveInstanceState और onRestoreInstanceState को पदानुक्रम की स्थिति को बचाने / पुनर्स्थापित करने में सक्षम होने के लिए एक अद्वितीय आईडी की आवश्यकता है। यदि दो विचारों में एक ही आईडी है, तो उनमें से एक की स्थिति खो जाएगी। इसलिए जब तक आप व्यू स्टेट को सेव नहीं करते हैं, सभी अपने आप में डुप्लिकेट आईडी होना एक अच्छा विचार नहीं है।
एमैनुएल मोएक्लिन

3
ईद अद्वितीय होना चाहिएएपीआई स्तर 17 से शुरू करके व्यू क्लास में एक स्थिर विधि है जो इसे व्यू आईडी के रूप में उपयोग करने के लिए एक यादृच्छिक आईडी बनाती है। यह विधि यह सुनिश्चित करती है कि निर्मित समय के दौरान aapt उपकरण द्वारा पहले से उत्पन्न किसी अन्य दृश्य आईडी के साथ उत्पन्न आईडी टकराएगी नहीं। डेवलपर
महमूद

576

एपीआई स्तर 17 और ऊपर से, आप कॉल कर सकते हैं: View.generateViewId ()

इसके बाद View.setId (int) का उपयोग करें ।

यदि आपका ऐप API स्तर 17 से कम लक्षित है, तो ViewCompat.generateViewId () का उपयोग करें


2
मैंने इसे अपने स्रोत कोड में डाल दिया क्योंकि हम निम्न एपीआई स्तरों का समर्थन करना चाहते हैं। यह काम कर रहा है लेकिन अनंत लूप एक अच्छा अभ्यास नहीं है।
SXC

5
@SimonXinCheng अनंत लूप एक सामान्य पैटर्न है जिसका उपयोग गैर अवरुद्ध एल्गोरिदम में किया जाता है। उदाहरण के लिए AtomicIntegerतरीकों के कार्यान्वयन पर एक नज़र है ।
आइडलोन

7
बहुत अच्छा काम करता है! एक नोट: मेरे प्रयोगों के आधार पर, आपको setId () से पहले एक मौजूदा लेआउट में दृश्य जोड़ना होगा, वरना OnClickListener ठीक से काम नहीं करेगा।
ल्यूक

4
धन्यवाद, आप बहुत छोटे होंगे लेकिन धन्यवाद। प्रश्न, for(;;)मैंने जो देखा है वह पहले कभी नहीं देखा। किसे कहते है?
अग्रेंजी

5
@ एग्रेसर: इसका एक खाली 'लूप' है।
sid_09

143

आप R.idएक xml संसाधन फ़ाइल का उपयोग करते हुए कक्षा में बाद में आईडी का उपयोग कर सकते हैं, और संकलन समय के दौरान एंड्रॉइड एसडीके उन्हें अद्वितीय मान दे सकते हैं।

 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"/>

कोड में इसका उपयोग करने के लिए:

myEditTextView.setId(R.id.my_edit_text_1);

20
यह काम नहीं करता है जब मेरे पास अज्ञात तत्वों की संख्या होती है जिन्हें मैं आईडी निर्दिष्ट करूंगा।
मूकिंग डक

1
@MooDDuck मुझे पता है कि यह एक साल देर से है, लेकिन जब मुझे अज्ञात संख्या में तत्वों के साथ रनटाइम पर अद्वितीय Ids असाइन करना होता है, तो मैं बस उपयोग करता हूं "int currentId = 1000; whateverView.setId(currentId++);- जो हर बार आईडी currentId++का उपयोग करता है, एक अद्वितीय आईडी सुनिश्चित करता है, और मैं स्टोर कर सकता हूं मेरे ArrayList में बाद में उपयोग के लिए आईडी।
SAT में

3
@ मायिकसैट: यह केवल गारंटी देता है कि वे आपस में अद्वितीय हैं। यह इसे नहीं बनाता है "इसलिए यह अन्य आईडी के साथ संघर्ष नहीं करता है", जो प्रश्न का एक महत्वपूर्ण हिस्सा है।
मूविंग डक

1
यह जीतने वाला उत्तर है क्योंकि अन्य लोग एंड्रॉइड स्टूडियो के कोड विश्लेषण उपकरण को एक --- फिट दे रहे थे, और क्योंकि मुझे एक आईडी की आवश्यकता है जो परीक्षणों को अभी तक एक और चर जोड़ने के बिना पता है। लेकिन जोड़ें <resources>
Phlip

62

इसके अलावा, आप को परिभाषित कर सकते ids.xmlमें res/values। आप Android के नमूना कोड में एक सटीक उदाहरण देख सकते हैं।

samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml

15
यहाँ इस दृष्टिकोण के साथ एक उत्तर भी दिया गया है: stackoverflow.com/questions/3216294/…
Ixx

संदर्भ के लिए, मुझे इसमें फ़ाइल मिली: /samples/android-15/ApiDemos/src/com/example/android/apis/view/RadioGroup1.java
टेलर


25

यह मेरे लिए काम करता है:

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

यह थोड़ा अधिक जटिल है लेकिन मुझे यकीन है कि यह काम करेगा। मल्टीथ्रेडेड वातावरण में वैश्विक चर का उपयोग करना निश्चित रूप से किसी दिन विफल हो जाएगा, खासकर कई कोर के साथ।
मातरिनस

3
इसके अलावा, यह संभवतः जटिल लेआउट के लिए धीमा नहीं है?
डैनियल रोड्रिगेज

15
findViewById()एक धीमा ऑपरेशन है। दृष्टिकोण काम करता है, लेकिन प्रदर्शन की कीमत पर।
किरील अलेक्जेंड्रोव

10

(यह dilettante के जवाब के लिए एक टिप्पणी थी लेकिन यह बहुत लंबा हो गया ... hehe)

बेशक एक स्थिर यहाँ की जरूरत नहीं है। आप स्थैतिक के बजाय, सहेजने के लिए SharedPreferences का उपयोग कर सकते हैं। किसी भी तरह से, कारण वर्तमान प्रगति को बचाने के लिए है ताकि इसकी जटिल लेआउट के लिए बहुत धीमा न हो। क्योंकि, वास्तव में, एक बार उपयोग करने के बाद, यह बाद में तेजी से होगा। हालांकि, मुझे नहीं लगता कि यह ऐसा करने का एक अच्छा तरीका है क्योंकि अगर आपको अपनी स्क्रीन को फिर से बनाना है (जैसा कि फिर से कहा onCreateजाता है), तो आप शायद किसी भी तरह से शुरुआत करना चाहते हैं, स्थैतिक की आवश्यकता को समाप्त करना। इसलिए, स्थैतिक के बजाय इसे केवल एक उदाहरण चर बनाएं।

यहां एक छोटा संस्करण है जो थोड़ा तेज चलता है और पढ़ने में आसान हो सकता है:

int fID = 0;

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

यह उपरोक्त कार्य पर्याप्त होना चाहिए। क्योंकि, जहां तक ​​मैं बता सकता हूं, एंड्रॉइड-जनरेटेड आईडी अरबों में हैं, इसलिए यह संभवत: 1पहली बार वापस आएगा और हमेशा काफी तेज होगा। क्योंकि, यह वास्तव में एक अप्रयुक्त को खोजने के लिए इस्तेमाल की गई आईडी के पिछले पाशन होना चाहिए। हालाँकि, लूप है कि यह वास्तव में एक इस्तेमाल किया आईडी मिलनी चाहिए।

हालाँकि, यदि आप अभी भी अपने ऐप के बाद के मनोरंजन के बीच प्रगति को सहेजना चाहते हैं, और स्थैतिक का उपयोग करने से बचना चाहते हैं। यहाँ साझाकरण संस्करण है:

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

इसी तरह के एक सवाल का जवाब आपको वह सब कुछ बता देना चाहिए जो आपको आईडी के बारे में जानना चाहिए: https://stackoverflow.com/a/13241629/693927

EDIT / FIX: बस एहसास हुआ कि मैं पूरी तरह से बचाने के लिए goofed। मैं नशे में धुत हो गया होगा।


1
यह शीर्ष उत्तर होना चाहिए। ++ कीवर्ड और खाली स्टेटमेंट का शानदार उपयोग;)
एरन बिलियन

9

'कम्पैटिबल' लाइब्रेरी अब generateViewId()एपीआई लेवल से पहले 17 के लिए विधि का भी समर्थन करती है ।

बस Compatपुस्तकालय के एक संस्करण का उपयोग करना सुनिश्चित करें जो है27.1.0+

उदाहरण के लिए, आपकी build.gradleफ़ाइल में:

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

फिर आप केवल कक्षा के बजाय कक्षा generateViewId()से निम्नानुसार उपयोग कर सकते हैं :ViewCompatView

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

हैप्पी कोडिंग!


6

@ हाथीकोमबेल के उत्तर के लिए एक अतिरिक्त,

जबकि View.generateViewId()API स्तर की आवश्यकता है> = 17,
यह उपकरण सभी एपीआई के साथ कंपैटिबल है।

वर्तमान एपीआई स्तर के अनुसार,
यह सिस्टम एपीआई या नहीं का उपयोग करके मौसम तय करता है।

इसलिए आप एक ही समय में उपयोग कर सकते हैं ViewIdGenerator.generateViewId()और View.generateViewId()एक ही आईडी प्राप्त करने के बारे में चिंता न करें

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

    }
}

@kenyee कोड स्निपेट for (;;) { … }Android स्रोत कोड से आता है।
फंतासी

मेरी समझ से सभी जनरेट किए गए आईडी नंबर 0x01000000-0xffffffff स्थान पर कब्जा कर लेते हैं, इसलिए आपको एक गैर-क्लैश की गारंटी दी जाती है, लेकिन मुझे याद नहीं है कि मैंने यह कहां पढ़ा है।
एंड्रयू वाइल्ड

कैसे रीसेट करें ..generateViewId()
reegan29

@kenyee के पास एक बिंदु है, यह व्यू क्लास के भीतर उत्पन्न आईडी से टकरा सकता है। मेरा उत्तर :) देखें
singed

else { return View.generateViewId(); }यह 17 उपकरणों की तुलना में छोटे एपीआई स्तर के लिए अनंत लूप में जाएगा?
ओकरकोज़

3

आदेश में गतिशील रूप से देखें आईडी फॉर्म एपीआई 17 का उपयोग करें

generateViewId ()

जो कि उपयोग के लिए उपयुक्त मूल्य उत्पन्न करेगा setId(int)। यह मान aapt द्वारा निर्मित समय पर बनाए गए ID मानों से नहीं टकराएगा R.id


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

1

मैं उपयोग करता हूं:

public synchronized int generateViewId() {
    Random rand = new Random();
    int id;
    while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
    return id;
}

एक यादृच्छिक संख्या का उपयोग करके मुझे हमेशा पहले प्रयास में अद्वितीय आईडी प्राप्त करने का एक बड़ा मौका मिलता है।


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

कृपया अपने उत्तर में एक उचित विवरण इस तरह से जोड़ें कि भविष्य के आगंतुकों को आपके उत्तर के मूल्य का आकलन करने के लिए स्पष्ट हो। कोड-ओनली उत्तर को फॉलो किया जाता है और समीक्षाओं के दौरान इसे हटाया जा सकता है। धन्यवाद!
लुइज़ क्रूज़

-1

मेरी पसंद:

// Method that could us an unique id

    int getUniqueId(){
        return (int)    
                SystemClock.currentThreadTimeMillis();    
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.