373

我在 for 循环中以编程方式添加 TextViews 并将它们添加到 ArrayList。

我该如何使用TextView.setId(int id)?我想出什么整数 ID 才不会与其他 ID 冲突?

4

15 回答 15

646

从 API 级别 17 及以上,您可以调用:View.generateViewId()

然后使用View.setId(int)

如果您的应用程序目标低于 API 级别 17,请使用ViewCompat.generateViewId()

于 2013-03-15T22:12:01.523 回答
174

您可以R.id使用 xml 资源文件定义稍后在课堂上使用的 ID,并让 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);
于 2014-04-05T05:13:59.720 回答
155

根据View文档

标识符在此视图的层次结构中不必是唯一的。标识符应该是一个正数。

所以你可以使用任何你喜欢的正整数,但在这种情况下,可能会有一些具有等效 ID 的视图。如果您想在层次结构中搜索某些视图,setTag使用某些关键对象调用可能会很方便。

于 2009-11-11T11:11:01.153 回答
62

您也可以ids.xmlres/values. 您可以在 android 的示例代码中看到一个确切的示例。

samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
于 2009-11-27T10:07:20.060 回答
31

从 API 17 开始,View该类有一个静态方法 generateViewId(),它将

生成适合在 setId(int) 中使用的值

于 2013-02-20T08:42:25.690 回答
26

这对我有用:

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++;  
}
于 2012-07-23T15:12:47.253 回答
11

(这是对业余爱好者回答的评论,但它太长了......呵呵)

当然,这里不需要静态。您可以使用 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

编辑/修复:刚刚意识到我完全搞砸了保存。我一定是喝醉了。

于 2013-01-26T22:02:37.767 回答
11

'Compat' 库现在还支持generateViewId()17 之前的 API 级别的方法。

只要确保使用的Compat库版本是27.1.0+

例如,在您的build.gradle文件中,输入:

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

然后你可以简单地使用generateViewId()fromViewCompat类而不是View类,如下所示:

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

快乐编码!

于 2018-07-20T20:18:24.707 回答
6

只是对@phantomlimb 答案的补充,

虽然View.generateViewId()要求 API 级别 >= 17,
但此工具与所有 API 兼容。

根据当前的 API Level
决定天气是否使用系统 API。

所以你可以同时使用ViewIdGenerator.generateViewId()andView.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 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();
        }

    }
}
于 2014-01-08T16:01:19.923 回答
5

为了动态生成 View Id form API 17 使用

生成ViewId()

这将生成一个适合在setId(int). 此值不会与 aapt for 在构建时生成的 ID 值冲突R.id

于 2013-08-13T11:52:26.460 回答
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();
        }
    }
}
于 2014-04-25T12:11:41.120 回答
2

受@dilettante 回答的启发,这是我在 kotlin 中作为扩展函数的解决方案:

/* sets a valid id that isn't in use */
fun View.findAndSetFirstValidId() {
    var i: Int
    do {
        i = Random.nextInt()
    } while (findViewById<View>(i) != null)
    id = i
}
于 2020-11-16T16:27:15.633 回答
1

我用:

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

通过使用随机数,我总是很有可能在第一次尝试时获得唯一 ID。

于 2016-12-19T22:38:44.997 回答
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);
}
于 2016-12-12T20:46:04.933 回答
-1

我的选择:

// Method that could us an unique id

    int getUniqueId(){
        return (int)    
                SystemClock.currentThreadTimeMillis();    
    }
于 2018-02-22T06:48:20.843 回答