Android:View.setID(int id)以编程方式 - 如何避免ID冲突?

znq*_*znq 305 android conflict android-view

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

我该怎么用TextView.setId(int id)?我提出了什么整数ID,所以它不与其他ID冲突?

Xia*_*ang 533

从API级别17及更高版本,您可以调用:View.generateViewId()

然后使用View.setId(int).

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

  • 效果很好!一个注意事项:根据我的实验,您必须在将视图添加到现有布局之前调用setId(),否则OnClickListener将无法正常工作. (6认同)
  • @SimonXinCheng无限循环是非阻塞算法中常用的模式.例如,看一下`AtomicInteger`方法的实现. (5认同)
  • @Aggressor:它是一个空的'for'循环. (5认同)
  • 谢谢你会太小但是谢谢你.问题是什么是`for(;;)`我之前从未见过.这叫什么? (4认同)
  • 我把它放在我的源代码中,因为我们想支持较低的API级别.它正在工作,但无限循环不是一个好习惯. (2认同)

Nik*_*nov 139

根据View文件

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

所以你可以使用你喜欢的任何正整数,但在这种情况下,可能会有一些具有相同id的视图.如果要在层次结构中搜索某些视图,则可以setTag使用某些关键对象进行调用.

  • -1我不同意这个答案,因为onSaveInstanceState和onRestoreInstanceState需要一个唯一的id才能保存/恢复视图层次结构的状态.如果两个视图具有相同的id,则其中一个视图的状态将丢失.所以,除非你保存View状态,否则你自己拥有重复的id并不是一个好主意. (45认同)
  • 我认为文档提到了这个问题.如果在同一层次结构中具有相同ID的视图,则`findViewById`将返回它找到的第一个. (25认同)
  • **Id 应该是唯一的**。从 **API 级别 17** 开始,View 类中有一个静态方法可以生成一个随机 ID 以将其用作视图 ID。该方法确保生成的 id 不会在构建期间与 aapt 工具已经生成的任何其他视图 id 冲突。https://developer.android.com/reference/android/view/View#generateViewId%28%29 (4认同)
  • 有趣的是,我不知道ID不一定是唯一的吗?那么`findViewById`是否可以保证,如果有多个具有相同ID的视图,则返回哪个视图?文档没有提到任何内容. (2认同)
  • @DanyY我不太确定我是否正确理解你的意思.我试图说的是,如果用`setContentView()`设置的布局让我们说,10个视图的id在同一层次结构**中设置为相同的id号,那么调用`findViewById([ repeated_id])`将返回第一个视图集,其中包含一个重复的id.我正是这个意思. (2认同)

Sai*_*tya 128

您可以R.id使用xml资源文件设置稍后在课堂上使用的ID ,并让Android SDK在编译期间为它们提供唯一值.

 res/values/ids.xml
Run Code Online (Sandbox Code Playgroud)

<item name="my_edit_text_1" type="id"/>
<item name="my_button_1" type="id"/>
<item name="my_time_picker_1" type="id"/>
Run Code Online (Sandbox Code Playgroud)

要在代码中使用它:

myEditTextView.setId(R.id.my_edit_text_1);
Run Code Online (Sandbox Code Playgroud)

  • 当我有一个未知数量的元素我将分配ID时,这不起作用. (18认同)
  • @MikeinSAT:那只能保证它们之间是唯一的。但这并不意味着“它不会与其他ID冲突”,这是问题的关键部分。 (2认同)
  • 这是获胜的答案,因为其他人对 Android Studio 的代码分析工具非常满意,而且因为我需要一个测试知道的 ID,而无需添加另一个变量。但添加 `&lt;resources&gt;`。 (2认同)

小智 60

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

samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Run Code Online (Sandbox Code Playgroud)

  • 这也是这种方法的答案:http://stackoverflow.com/questions/3216294/android-programatically-add-id-to-r-id (15认同)

Die*_*rik 27

由于API 17中,View类有一个静态方法 generateViewId()是将

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


dil*_*nte 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++;  
}
Run Code Online (Sandbox Code Playgroud)

  • ```findViewById()```是一个缓慢的操作.该方法有效,但代价是性能. (14认同)
  • 此外,对于复杂的布局,这可能不是很慢吗? (3认同)

Pim*_*kit 10

(这是对dilettante的答案的评论,但它太长了......呵呵)

当然,这里不需要静电.您可以使用SharedPreferences来保存,而不是静态.无论哪种方式,原因是保存当前进度,以便它对于复杂的布局而言不会太慢.事实上,因为在使用一次后,它会在以后相当快.但是,我不觉得这是一个很好的方法,因为如果你必须重新重建你的屏幕(比如onCreate再次调用),那么你可能想从头开始,无论如何,不​​需要静态.因此,只需将其设为实例变量而不是静态变量.

这是一个较小的版本,运行速度更快,可能更容易阅读:

int fID = 0;

public int findUnusedId() {
    while( findViewById(++fID) != null );
    return fID;
}
Run Code Online (Sandbox Code Playgroud)

上述功能应该足够了.因为,据我所知,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;
}
Run Code Online (Sandbox Code Playgroud)

这个类似问题的答案应告诉你需要知道的关于android的所有ID:https://stackoverflow.com/a/13241629/693927

EDIT/FIX:刚刚意识到我完全搞砸了.我一定是喝醉了.


小智 8

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

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

例如,在您的build.gradle文件中,添加:

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

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

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

快乐的编码!


fan*_*uch 6

只是@phantomlimb答案的补充,

虽然View.generateViewId()要求API级别> = 17,但
此工具与所有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 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();
        }

    }
}
Run Code Online (Sandbox Code Playgroud)


Aru*_*n C 5

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

生成ViewId()

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