Android 布局引用 xml 文件后面的元素

Android layout reference xml element later in file

如何引用后面的 XML 元素?

这是一个具体的用例。假设我有一个带有根 LinearLayout 的表单,包含多行的 LinearLayouts,每行有一个或多个文本输入区域。

这是我想要的视觉效果。第一张图片来自 Venmo 的应用程序,第二张是以下效果图 XML.

这样的布局可能如下所示:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/row_card_number"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/card_number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:nextFocusDown="@id/month"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/row_date"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/month"
            android:layout_height="wrap_content"
            android:layout_width="100dp"
            android:nextFocusDown="@id/year"/>

        <EditText
            android:id="@+id/year"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"/>
    </LinearLayout>
</LinearLayout>

在这个用例中,前向引用是必要的,以便设置下一个焦点元素。这样,当您按下键盘上的下一个按钮时,它会转到正确的视图。在此示例 xml 中,如果没有 nextFocusDown,按下一步将从名称转到月份,而永远不会转到年份。

但是,如果您尝试编译它,您会得到一个错误:

Error:(18, 36) No resource found that matches the given name (at 'nextFocusDown' with value '@id/month').

这是因为当我试图引用它时,id month 还没有被初始化,因为它在文件的后面。如何引用 xml 中稍后出现在文件中的 ID?

最简单的解决方案就是更换

android:nextFocusDown="@id/month"

android:nextFocusDown="@+id/month"

当编译器解析您的 XML 以将 id 添加到 R.java 时,它只是从上到下读取。当您有 @id/month 时,它会搜索现有的 ID,但找不到它。

但是,如果您这样做 @+id/month,它会创建一个新 ID,并链接到该 ID。当它到达实际月视图中的 android:id=@+id/month 时,它会将其链接到我们已经创建的同一 ID。


这就提出了一个问题:如果你可以用 @+id/ 替换 @id/,并且 @+id/ 无论元素的顺序如何都可以工作,为什么还要费心使用 @id/?

原因是如果 id 不存在,@id/ 将抛出编译器错误,而 @+id/ 将在运行时记录警告。

考虑这个 XML:

<EditText
    android:id="@+id/month"
    android:layout_height="wrap_content"
    android:layout_width="100dp"
    android:nextFocusDown="@+id/SOME_RANDOM_ID"/>

<EditText
    android:id="@+id/year"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"/>

解析后,将创建一个新的 id 元素 SOME_RANDOM_ID。但是,当 Android 尝试在运行时应用它时,它找不到具有该 ID 的元素。如果您查看 Logcat,您会看到:

W/View﹕ couldn't find view with id 2131689604

此日志消息既难以查找又难以调试。 @+id/ 中的一个小错字,您将遇到一个非常难以调试的错误。但是,如果我们这样做了:

android:nextFocusDown="@id/SOME_RANDOM_ID"

然后我们会得到一个编译器错误,类似于:

Error:(18, 36) No resource found that matches the given name (at 'nextFocusDown' with value '@id/SOME_RANDOM_ID').

这更容易查找和调试。


tl;dr:您可以使用 @+id/ 而不是 @id/ 并且您将能够转发参考,但请注意,这会使小的拼写错误变得非常难以调试。

您也许可以使用 RelativeLayout 使所有视图以相反的顺序存在于 xml 中,但这对我来说似乎有点过分了。

我最近遇到了同样的问题,我第一次引用元素时使用了@+id/my_new_id,后来在元素中的XML定义,我将 @id/my_new_id 分配给 android:id 属性。看起来它工作正常并且没有必要多次使用相同的 id 编写 @+id 以避免可能的警告。

例如:

<LinearLayout
    ...
    android:layout_toLeftOf="@+id/my_new_id"
    ... >

...

</LinearLayout>

<ImageButton
    android:id="@id/my_new_id"
    ... />