Why declaring #+id is allowed in other than id attributes - android

So lastly I have had to rebuild not my XML layout file for android app. And I saw constructions like
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/rev_main"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/rev_arrow">
<!-- some stuff in here -->
</LinearLayout>
<ImageView
android:id="#+id/rev_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/arrow"
/>
</RelativeLayout>
It is really annoying when I see that someone using #+id in not android:id attribute. When any developer will want to search this id he will find LinearLayout first instead ImageView.
My question is why google allow it? Is there any particular reason for it and I just didn't know it?
Sorry for my poor English.

Yes, there is a reason:
Sometimes, you have to set a View A relative to the position of a View B which is declared later in the XML-file (which is the case in your example. The LinearLayout is "View A" and the ImageView is "View B").
Imagine, the code you've got a problem with
android:layout_below="#+id/rev_arrow"
would look like this instead:
android:layout_above="#+id/rev_arrow"
The android:layout_above would be useless if you couldn't declare an id inside it.
Because it was mentioned in a few comments:
You have to use the "plus"-sign always at that place, where the id is first declared in the layout-file (from top to bottom). It is independet from the attribute, like "id" or "layout_below".

This is a valid use of the ID, as it tells the layout manager that the view identified by id/rev_main view is to be placed below the view identified by id/rev_arrow.
So in places other than android:id, the ids are used as references to views identified by the respective id.

Related

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.
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?
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.
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"
... />

Two views referencing each other

I have two textviews declared as below. My intention is to have two views side by side, where left view (displaying user name) has bigger text size, right view displays time of last message send and should be always visible (even if user has really long name, that's why I'm using android:layout_toLeftOf). However, left view is smaller and I want to align its baseline to right view. It's really nice dependency where and I'm not able to solve it.
Partially acceptable solution is to use "android:layout_toRightOf" in right view, but if user has really long name, then time (right view) will be ellipsized (it's declared in AppTheme.TextView.SingleLine).
So basically, my questions is, is it possible for two views to reference each other? I understand why I'm getting this error, but I'm not able to solve it.
I remember from my C/C++ times that it was possible to declare function in the top of the file and then define it somewhere else (so the compiler doesn't complain) and I think it's something what I need here.
<TextView
android:id="#+id/fragment_messages_item_sender_name"
style="#style/AppTheme.TextView.SingleLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#id/fragment_messages_item_last_msg_time"
android:textSize="#dimen/global_text_large"/>
<TextView
android:id="#+id/fragment_messages_item_last_msg_time"
style="#style/AppTheme.TextView.SingleLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#id/fragment_messages_item_sender_name"
android:layout_alignParentEnd="true"
android:gravity="right"/>
All I'm getting is
Error:(27, 38) No resource found that matches the given name (at 'layout_toLeftOf' with value '#id/fragment_messages_item_last_msg_time').
in R class id like
fragment_messages_item_last_msg_time
not exist
for avoid that problem need to use "+" before declaration field with id relation
android:layout_toLeftOf="+#id/fragment_messages_item_last_msg_time"
you could move the android:layout_alignBaseline in the other TextView. Be aware of id loops, that usually generated nasty crashes.
About your issue, you have to remember the entries inside R are marked as public static final, and that the + generates a new entry for the specify id, if it does not exists, So you can have:
android:layout_toLeftOf="#+id/fragment_messages_item_last_msg_time"
in the first TextView, and
android:id="#id/fragment_messages_item_last_msg_time"
to the second one. As I mentioned before, loops in RelativeLayout are not allowed, and those will make your app crash
Hi use below code :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="#+id/fragment_messages_item_sender_name"
style="#style/AppTheme.TextView.SingleLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Manish" />
<TextView
android:id="#+id/fragment_messages_item_last_msg_time"
style="#style/AppTheme.TextView.SingleLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Khamar" />
</LinearLayout>

Global or local id for views inside a layout?

When assigning an id to a view of a layout in an xml resource, I usually do something like this:
(example)
contact_info.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" >
<TextView
android:id="#+id/contact_info_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Here the TextView contains the name of a contact.
Is it better to use a complete id like that or just "name" is enough?
Appropriate convention would be like this:
From the example you have provided, the appropriate id for the text view would be: contactinfo_textview_name
Please note that, its NOT rule. You can use anything you are comfortable with! cheers
As I indicated Hardik4560, it is best to names that are sufficiently descriptive and not too long.
Realize that your application can grow much (who knows!?) and there may come a time when no longer know to which each identifier belongs layout.
Regards!

Getting ViewStub with #android:id/empty in Monodroid returns null

In Monodroid I have a ListActivity class with the following axml segment:
<ListView android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub
android:id="#android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout="#layout/empty" />
I'm trying to access the ViewStub with:
FindViewById(Android.Resource.Id.Empty);
However, it always returns null. If I access the list with:
FindViewById(Android.Resource.Id.List);
it works or if I name the ViewStub something other than #android:id/empty it works. The ViewStub does work when my list is empty.
Thanks
Here are a few things I can think of that might be the issue.
The first issue could be that you have another UI page that defined the id of "empty" somewhere else. MonoDroid puts all IDs from all pages into the same resource file, so you obviously can't have the same name twice. Try putting empty2 to see if that works.
The next issue could be that you should have android:id=#id+/empty". The + tells MonoDroid to generate an ID for the control in the Resource file.
Last, use something other than "empty". Empty could be a reserved word by Android/MonoDroid, so I would avoid it.

Android: How to use an xml string value to determine layout orientation

I have a simple linear layout that I would like to change based on the screen size of the device. What I am trying to do is something like
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="#string/cover_orientation"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
I have created different dimen.xml files under values and values-xlarge so that the cover_orientation variable will take on a different value (either 'vertical' or 'horizontal') based on the screen size.
string name="cover_orientation">"vertical"
But this doesn't work. I have found a temporary work around that involves checking the screen size and changing the orientation manually:
if(getResources().getString(R.string.screen_size) == "xlarge"){
((LinearLayout)convertView).setOrientation(1);
}
but it seems like you should be able to do it the first way (much more elegant/less code).
I considered just having a different layout for each screen size, but the layout is actually quite big and this is the only change I need for the different screen sizes. So it didn't make much sense to me to duplicate the entire layout.
Thanks!
A nice way to do this is to add
android:orientation="#integer/cover_orientation"
on your LinearLayout and defining it like below.
in values/consts.xml:
<resources>
<integer name="orientation_horizontal">0</integer>
<integer name="orientation_vertical">1</integer>
</resources>
in values/something.xml:
<resources>
<integer name="cover_orientation">#integer/orientation_vertical</integer>
</resources>
in values-land/something.xml:
<resources>
<integer name="cover_orientation">#integer/orientation_horizontal</integer>
</resources>
This way you avoid hardcoding zeros and ones in your orientation variable definitions across the app.
I eventually found a solution to the problem by looking around the android docs. What I originally had was a LinearLayout, that contained an image an text inside of it (there was actually a lot more content inside of it but I'll keep it simple for this example), that looked something like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView android:id="#+id/cover"
android:layout_width="#dimen/cover_width"
android:layout_height="#dimen/cover_height"/>
<TextView android:id="#+id/title"
android:gravity="top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="#dimen/title_font_size"
android:scrollHorizontally="true"
android:maxLines="1"
android:ellipsize="end" />
</LinearLayout>
What I wanted was to be able to dynamically change whether the text was beside the image or below the image, based on the device screen size. In other words i wanted to dynamically change the android:orientation dynamically. My first thought, which I posted in my question, was to have a string variable declared in the res/values/dimen.xml as
<string name="orientation">horizontal</string>
and another string variable declared in res/values-large/dimen.xml as
<string name="orientation">vertical</string>
Then when I was setting the orientation in the LinearLayout I thought I could use
android:orientation="#string/orientation"
But this didn't work. What I ended up doing was splitting the layout up. I originally had reservations about having two separate layouts because I thought I would have a lot of duplicated code for one simple change. That was before I learned about include and merge. First I created a common layout file that was the image and text in res/layout/content.xml that looked like:
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<ImageView android:id="#+id/cover"
android:layout_width="#dimen/cover_width"
android:layout_height="#dimen/cover_height"/>
<TextView android:id="#+id/title"
android:gravity="top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="#dimen/title_font_size"
android:scrollHorizontally="true"
android:maxLines="1"
android:ellipsize="end" />
</merge>
(Sidenote: I was originally confused at what the merge tag did. It is not merging what is inside the merge tag (in this example the image and the text) it is basically saying whatever parent file includes this file, merge the contents in between the tags into the parent file)
Then I created two separate files for just the LinearLayout that included the image and description xml file. One in res/layout/container.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/library_item_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<include layout="#layout/content"/>
</LinearLayout>
and one in res/layout-large/container.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/library_item_container"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<include layout="#layout/library_item_contents"/>
</LinearLayout>
Notice the only difference between the two container.xml files is that the orientation has changed from vertical to horizontal. Now there is minimal code that is repeated and problem is solved!
#odiggity, thank you for posting this question. I was trying the same. Application crashed upon starting.
I would assume that it's a runtime typing issue. In other words, there are only two legal values for the orientation attribute, which is not reflected by the string "type". What the framework would probably have to do is introduce another specialized resource type, similar to dimen or boolean.
I feel there is an answer to your question which addresses more cases than your own answer above. One can use style inheritance to define all attributes except orientation in an orientation-independent parent style and then add only the orientation in a small orientation-dependent style definition with that as a parent.
This way, one can avoid duplication even in complex cases.
I suggest you do some reading: http://developer.android.com/guide/practices/screens_support.html

Categories

Resources