2009-11-11 60 views

回答

121

根據View文檔

標識符不必在此視圖的層次結構獨特。標識符應該是一個正數。

所以你可以使用你喜歡的任何正整數,但在這種情況下,可以有一些視圖與等效的ID。如果您想搜索層次結構中的某個視圖,調用setTag,某些關鍵對象可能會很方便。

+1

有趣的,我不知道,標識不必是唯一的?那麼'findViewById'做出任何保證,那麼如果有多個相同的ID,那麼返回哪個視圖?文檔沒有提及任何內容。 – Matthias 2012-05-03 10:04:55

+22

我認爲文檔提到了這件事。如果在同一層次結構中具有相同ID的視圖,則findViewById將返回它找到的第一個ID。 – kaneda 2012-05-07 14:32:11

+0

@kaneda我不認爲它獲得第一個ID。它會得到你在setContentView()中設置的佈局中的id – 2013-06-19 16:15:41

58

您還可以在res/values中定義ids.xml。你可以在android的示例代碼中看到一個確切的例子。

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

這裏也是這個方法的答案:http://stackoverflow.com/questions/3216294/android-programatically-add-id-to-r-id – Ixx 2012-04-21 10:57:33

+0

作爲參考,我發現文件在: /samples/android -15/ApiDemos/src/com/example/android/apis/view/RadioGroup1.java – 2012-05-28 04:00:56

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

哇,簡直是親切! – max4ever 2012-08-10 10:49:02

+0

[This](http://stackoverflow.com/a/6790714/581205)有點複雜,但我敢打賭它會工作。在多線程環境中使用全局變量肯定會在某一天失敗,尤其是在多核心的情況下。 – maaartinus 2012-09-14 22:21:38

+3

另外,這對於複雜的佈局可能不是很慢嗎? – 2013-01-11 00:17:53

10

(這是外行的答案註釋,但它得到了太久......嘿嘿)

當然,此處不需要靜態。你可以使用SharedPreferences來保存,而不是靜態的。無論哪種方式,原因是要保存當前的進度,以便其複雜的佈局不會太慢。因爲事實上在使用過一次之後,它會在以後相當快。但是,我不覺得這是一個很好的方法,因爲如果你不得不重新生成屏幕(例如onCreate再次被調用),那麼你可能想從頭開始,無論如何,無需靜態。因此,只需使其成爲實例變量而不是靜態變量。

這裏是運行快一點,可能更容易閱讀的縮小版:

int fID = 0; 

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

這上面的函數應該是足夠的。因爲,據我所知,Android生成的ID數十億,所以這可能會第一次返回1,並且總是很快。 因爲它實際上不會循環使用已用的ID來找到一個未使用的ID。但是,循環它應該實際上找到一個使用的ID。

但是,如果您仍然希望在應用程序後續重新創建之間保存進度,並且希望避免使用靜態。這裏是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; 
} 

這回答類似的問題應該告訴你,你需要了解與Android ID的一切:https://stackoverflow.com/a/13241629/693927

編輯/ FIX:剛剛意識到我完全瘋玩起來保存。我一定喝醉了。

+1

這應該是最好的答案。大量使用++關鍵字和空語句;) – 2015-04-11 00:07:33

19

由於API 17,View類有一個static methodgenerateViewId(),將

產生適合於在SETID(INT)使用

410

谷歌終於實現生成用於唯一ID的需要的值以編程方式創建的視圖...

從API級別17及以上,您可以撥打電話

View.generateViewId()

然後使用View.setId(int)

情況下你需要它比17水平低的目標,這裏是其在View.java內部實現,你可以在你的項目中直接使用,把它放在你的Util類或某處:

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

ID大於0x00FFFFFF的數字保留給/ res xml文件中定義的靜態視圖。 (最有可能在我的項目****** 0x7f的從R.java)

在你的代碼,你可以這樣做:

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

     myView.setId(Utils.generateViewId()); 

    } else { 

     myView.setId(View.generateViewId()); 

    } 
+2

我把它放在我的源代碼中,因爲我們想支持較低的API級別。它正在工作,但無限循環不是一個好的做法。 – SXC 2013-09-19 00:07:21

+5

@SimonXinCheng無限循環是非阻塞算法中常用的一種模式。例如,看看'AtomicInteger'方法的實現。 – Idolon 2013-11-14 14:22:05

+5

很棒!一個注意:根據我的實驗,必須在將視圖添加到現有佈局之前調用setId(),否則OnClickListener將無法正常工作。 – Luke 2013-11-20 09:46:42

3

爲了動態生成視圖ID形式API使用17

generateViewId()

這將產生適合於在使用setId(int)的值。該值不會與構建時生成的ID值相沖突,適用於R.id

6

只是一個除了@phantomlimb的答案,

View.generateViewId()需要API等級> = 17,
這個工具是compatibe所有的API。

根據當前API級別,
它決定使用系統API的天氣與否。

所以你可以用在 同時ViewIdGenerator.generateViewId()View.generateViewId(),不擔心會相同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 [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(); 
     } 

    } 
} 
+1

這是如何避免在當前視圖w/API <17的情況下與視圖ID衝突? – kenyee 2014-04-22 13:50:11

+0

@kenyee代碼片段(for;){...}'來自Android源代碼。 – fantouch 2014-05-06 07:28:57

+0

我的理解是所有生成的ID佔用數字空間0x01000000-0xffffffff,所以你保證沒有衝突,但我不記得我在哪裏讀這個。 – 2015-03-20 16:13:03

99

您可以設置ID,你會在R.id類使用XML資源文件後使用的,並讓Android SDK在編譯期間爲它們提供唯一值。

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); 
+11

當我擁有未知數量的要分配ID的元素時,這不起作用。 – 2015-10-13 18:44:21

+0

@MooingDuck我知道這是晚了一年,但是當我必須在運行時爲未知數量的元素分配獨特的ID時,我只需使用'「int currentId = 1000; whateverView.setId(currentId ++);' - 這會增加ID每當使用'currentId ++'時,確保一個唯一的ID,並且我可以將這些ID存儲在我的ArrayList中以供以後訪問。 – 2016-12-12 15:59:07

+0

@MikeinSAT:這隻能保證它們在它們之間是唯一的。它不會與其他ID衝突「,這是問題的關鍵部分 – 2016-12-13 03:15:24

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

請以對未來訪問者更清晰的方式爲您的答案添加一個正確的描述,以評估您的答案的價值。在評論期間可以刪除,謝謝! – 2016-12-12 22:41:15

0

我用:

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

通過使用一個隨機數,我總是有一個龐大的獲得唯一的ID在第一次嘗試的機會。

0

我的選擇:

// Method that could us an unique id 

    int getUniqueId(){ 
     return (int)  
       SystemClock.currentThreadTimeMillis();  
    }