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.
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 nextFocusDown
s, 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?
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With