Android layout reference xml element later in file
Asked Answered
R

2

6

How do I reference a later XML element?

Here's a specific use case. Let's say I have a form with a root LinearLayout, containing LinearLayouts for multiple rows, each row having one or more text input areas.

Here's a visual of what I'm going for. First pic is from Venmo's app, second is a rendering of the following XML.

Example of Venmo The layout

Such a layout could look like this:

<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>

In this use case, forward referencing is necassary in order to set the next focus element. That way, when you press the next button on the keyboard, it'll go to the correct view. In this sample xml, without the nextFocusDowns, pressing next would go from name to month, and never go to year.

However, if you try to compile this, you'll get an error:

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

This is because the id month hasn't yet been initialized when I'm trying to reference it, since that's later in the file. How can I reference an id in xml that appears later in the file?

Rateable answered 4/8, 2015 at 20:27 Comment(1)
This is a self answered question. I didn't really find anything else that explained this well, so I thought I'd do it myself. Suggestions for improvement are welcome :)Rateable
R
12

The simplest solution is just to replace

android:nextFocusDown="@id/month"

with

android:nextFocusDown="@+id/month"

When the compiler is parsing your XML to add the id's to R.java, it just reads top to bottom. When you have @id/month, it searches through the existing id's, and fails to find it.

However, if you do @+id/month, it creates a new id, and links to that. When it gets to android:id=@+id/month in the actual month view, it links it to the same id that we already created.


This brings up the question: If you can replace @id/ with @+id/, and @+id/ will work regardless of the order of elements, why even bother to use @id/?

The reason for this is if the id doesn't exist, @id/ will throw a compiler error, while @+id/ will log a warning at runtime.

Consider this 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"/>

When this is parsed, a new id element SOME_RANDOM_ID is created. However, when Android tries to apply it at runtime, it can't find an element with that id. If you look at Logcat, you'll see this:

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

This log message is both hard to find and hard to debug. One small typo in a @+id/ and you'll have a bug that could be incredibly difficult to debug. However, if we had done:

android:nextFocusDown="@id/SOME_RANDOM_ID"

Then we'd get a compiler error, something like:

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

This is much easier to find and debug.


tl;dr: You can use @+id/ instead of @id/ and you'll be able to forward reference, but note that that can make small typos incredibly difficult to debug.

You might be able to use a RelativeLayout to make all the Views exist in reverse order in the xml, but that seems like overkill to me.

Rateable answered 4/8, 2015 at 20:27 Comment(0)
S
0

I had the same issue recently and I used @+id/my_new_id the first time I referenced the element and later in the XML in the element definition, I assigned @id/my_new_id to the android:id attribute. It seems it works fine and it's not necessary write @+id with the same id more than one time avoiding possible warnings.

For example:

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

...

</LinearLayout>

<ImageButton
    android:id="@id/my_new_id"
    ... />
Somali answered 25/1, 2017 at 18:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.