Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android layout reference xml element later in file

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?

like image 573
Anubian Noob Avatar asked Aug 04 '15 20:08

Anubian Noob


People also ask

What tag should you use to add a reusable view component to a layout file?

To efficiently reuse complete layouts, you can use the <include/> and <merge/> tags to embed another layout inside the current layout. Reusing layouts is particularly powerful as it allows you to create reusable complex layouts.

What is the difference between relative layout and linear layout?

LinearLayout : is a ViewGroup that aligns all children in a single direction, vertically or horizontally. RelativeLayout : is a ViewGroup that displays child views in relative positions. AbsoluteLayout : allows us to specify the exact location of the child views and widgets.

What is a ViewGroup in android?

ViewGroup is a collection of Views(TextView, EditText, ListView, etc..), somewhat like a container. A View object is a component of the user interface (UI) like a button or a text box, and it's also called a widget.


1 Answers

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.

like image 80
Anubian Noob Avatar answered Sep 20 '22 00:09

Anubian Noob