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