如何在程序中为视图分配一个ID?

212
在XML文件中,我们可以像这样给视图分配一个ID:android:id="@+id/something",然后调用findViewById(),但是当程序化地创建视图时,我该如何指定ID?
我认为setId()不同于默认赋值。 setId()是额外的。
可以有人纠正我吗?

1
子集:如何生成唯一的ID:https://dev59.com/questions/oXI-5IYBdhLWcg3wta1q - Ciro Santilli OurBigBook.com
3个回答

537

Android id 概述

Android id 是通常用于标识视图的整数; 可以通过 XML (如果可能)和通过代码(编程方式)分配此 id。对于通过 Inflater 生成的 XML 定义的 View (例如使用 setContentView),id 最有用。

通过 XML 分配 id

  • 将属性 android:id="@+id/somename" 添加到您的视图中。
  • 构建应用程序时,将为 android:id 分配一个唯一的 int 以供在代码中使用。
  • 使用“R.id.somename” 引用代码中的 android:idint 值 (相当于一个常量)。
  • int 可能会因版本不同而发生变化,所以永远不要从 gen/package.name/R.java 复制 id,只需使用 "R.id.somename"。
  • (另外,在 XML 中分配给 Preferenceid 在生成其 View 时不会使用。)

通过代码分配 id

  • 使用 someView.setId(int); 手动设置 id
  • int 必须为正数,但是可以任意选择——它可以是您想要的任何东西(如果这很可怕,请继续阅读)。
  • 例如,如果创建并编号多个代表项目的视图,则可以使用它们的项目编号。

id 的唯一性

  • 通过 XML 分配的 id 将是唯一的。
  • 通过代码分配的 id 不必唯一。
  • 理论上说,通过代码分配的 id 可能会与通过 XML 分配的 id 冲突。
  • 如果正确查询(请继续阅读),则这些冲突的 id 将无关紧要。

当(以及为什么)冲突的 id 不重要

  • findViewById(int) 将从您指定的 View 开始递归深度优先遍历视图层次结构,并返回第一个具有匹配的 idView
  • 只要在层次结构中的 XML 定义的 id 之前没有分配代码分配的 idfindViewById(R.id.somename) 将始终返回 XML 定义的已 id 化的 View。

动态创建视图并分配 ID

  • 在布局 XML 中,定义一个带有 id 的空的 ViewGroup
  • 例如,使用 android:id="@+id/placeholder"LinearLayout
  • 使用代码为占位符 ViewGroup
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <item name="reservedNamedId" type="id"/>
    </resources>
    

    一旦 ViewGroup 或 View 创建完成,您可以附加自定义 ID。

    myViewGroup.setId(R.id.reservedNamedId);
    

    冲突的 id 示例

    为了更好地理解,我们来看看当后台存在id冲突时会发生什么。

    layout/mylayout.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
        <LinearLayout
            android:id="@+id/placeholder"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
    </LinearLayout>
    

    为了模拟冲突,假设我们的最新构建将R.id.placeholder (@+id/placeholder)分配给一个int 12..

    接下来,MyActivity.java通过代码以编程方式定义一些视图添加:

    int placeholderId = R.id.placeholder; // placeholderId==12
    // returns *placeholder* which has id==12:
    ViewGroup placeholder = (ViewGroup)this.findViewById(placeholderId);
    for (int i=0; i<20; i++){
        TextView tv = new TextView(this.getApplicationContext());
        // One new TextView will also be assigned an id==12:
        tv.setId(i);
        placeholder.addView(tv);
    }
    

    所以,placeholder 和我们新的 TextView 都有一个 id 为 12!但是如果我们查询 placeholder 的子视图,这并不是真正的问题:

    // Will return a generated TextView:
     placeholder.findViewById(12);
    
    // Whereas this will return the ViewGroup *placeholder*;
    // as long as its R.id remains 12: 
    Activity.this.findViewById(12);
    

    *还不错


10
除此之外,对于那些编写类似解决方案的人来说,了解在API > 17中使用View.generateViewId()可以生成不冲突的ID非常有用。 - AllDayAmazing
请注意,findViewById 进行深度优先搜索,因此“只要在层次结构中没有分配代码 ID 分配给 XML 定义的 ID 以上”并不是技术上正确的;它应该是“之前”而不是“以上”。 - Karu
在更近期的Android开发工具版本中,以编程方式将ID设置为任意值将被标记为编译器错误。该值应为实际资源ID。 - ThomasW
我相信即使在五年前回答这个问题时也是如此 - 这就是为什么必须在ids.xml中定义自定义ID的原因。对于真正的任意ID,请使用View.generateViewId()(API 17)。 (如果我错过了您的观点,请澄清一下。) - CodeShane
(此外,在 XML 中分配给首选项的 ID 在首选项生成其视图时不使用。)非常感兴趣。我的代码继承自 PreferenceDialogFragmentCompat,似乎来自 R.id 的 ID 与视图层次结构中的 ID 不匹配。这样我就无法通过 ID 找到视图。 - UrK

6
你可以使用View.setId(integer)来实现这一点。在XML中,即使你设置的是一个String id,它也会被转换成整数。因此,你可以为通过编程方式添加的Views使用任何(正)整数。
根据View文档,标识符不必在该视图的层次结构中是唯一的。标识符应该是一个正数。所以你可以使用任何你喜欢的正整数,但在这种情况下可能会有一些具有相同id的视图。如果你想在层次结构中搜索某个视图,则可以调用setTag,并使用某些关键对象。
感谢这个答案

5

是的,您可以在任何视图中调用setId(value),并使用您喜欢的任何(正整数)值在父容器中查找它,但请注意,对于不同的兄弟姐妹视图,使用相同的值调用setId()是有效的,但是findViewById()仅返回第一个。


1
请注意,您必须使用大于零的整数。 - Peter Ajtai
尽管第一个答案中指定的 findViewById(int) 方法会从您指定的 View 开始递归遍历整个视图层次结构,并返回具有匹配 id 的第一个 View,但这是最准确的描述。 - Aniket Thakur
是的,出于性能考虑,在已知的祖先上调用findViewById是一个好主意,但这并不能保证它会找到具有正确ID的直接子项。 - Karu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接