Do Android's R.id need to be unique? - android

Do Android's R.id need to be unique (as I've kept them so far)?
Or is it OK to reuse an R.id across different views?

ID's are used for finding views inside a view hierarchy. Android uses depth-first search algorithm - meaning it looks to the very bottom of one tree branch, then to another one etc. If you have two views with same ID, then the algorithm will find first view and stop searching further.
There is no strict requirement on uniqueness of ID. For instance, when you have a list view, then each item of that list will be inflated using same layout and in most cases will have same shared ID, which is totally ok.
Keeping that in mind, if you have two (or more) views sharing same ID, you should help Android to pick up the right one. For that you will first need to search for the correct parent of that view, and then for the view itself.
For instance, if you have two views with the same ID in two different fragments, then you should first search for fragment container view, and then for the view with shared ID inside that container.

Yes, it is OK to have the same ID in different layouts. You could find more info here: http://developer.android.com/guide/topics/ui/declaring-layout.html
Any View object may have an integer ID associated with it, to uniquely identify the View within the tree. When the application is compiled, this ID is referenced as an integer, but the ID is typically assigned in the layout XML file as a string, in the id attribute.
In a relative layout, sibling views can define their layout relative to another sibling view, which is referenced by the unique ID.
An ID need not be unique throughout the entire tree, but it should be unique within the part of the tree you are searching (which may often be the entire tree, so it's best to be completely unique when possible).

It is ok to use same id with different layouts.
For eg :
a.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.a);
b.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.b);
a.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/id_rel"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="4dp"
>
b.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/id_rel"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="4dp"
>
But using same id in one layout can cause exception

For most cases you can reuse id's in different layouts. But you should be aware about possibility of including one layout into another using , tags or using custom compund views as well as list items or fragments added to current view.

Related

Is it bad proactive to create backing subclasses for your layouts if they don't need customization?

I'm new to Android and have recently started adopting the pattern to create an auto-layout-loading custom view based on a layout file. In the layout you use the 'merge' tag as the root, then in the constructor of your view, you inflate that layout into yourself so you are essentially the root of the merged-in controls. Right after you inflate, still in the constructor, you can find the child controls from the layout and assign them to private fields in the custom class so they will always be available, even when using recycler views.
Contrast that with including a layout. In that case, the layout has to define a root element (which can be your custom control, but in that case, you would not inflate the layout into yourself and would have to move the control lookup to onFinishInflate) but otherwise it ends up with the same view hierarchy.
In short, instead of this...
<include layout="#layout/layout_myCustomControl" />
You can now do this...
<com.mydomain.MyCustomControl />
However I find the auto-loading custom control version to be much more flexible and maintainable. The advantages of the second one are not only that you can use custom attributes in the referencing XML layouts (which you can't with the 'include' version) but it also gives you a central place to manage the code as well as layout management/control lookup.
So now I can do this...
<com.mydomain.MyCustomControl
app:myCustomAttribute="Foo" />
And if the control has to have code backing up its behavior, it's nicely encapsulated in the custom view.
Here's an example layout for the auto-loading version...
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is static text in the header" />
<TextView android:id="#+id/dateTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</merge>
And here's the class that uses it...
public class MainHeader extends LinearLayout
{
TextView dateTextView;
public MainHeader(Context context, #Nullable AttributeSet attrs)
{
super(context, attrs);
setOrientation(VERTICAL);
LayoutInflater.from(context).inflate(R.layout.header_main, this);
dateTextView = (TextView)findViewById(R.id.dateTextView);
dateTextView.setText(<<Code for setting date goes here>>);
}
}
But what I'm wondering is if the layout is purely static in nature--say holding a static image and fixed text--is it still considered good practice to use a custom view to represent it? I'd say yes for consistency, plus should I want to extend it in the future, you're already prepared for it and have to do so in just one place regardless of how many places it's used. In contrast, if you included the layout directly in say 20 places, you may have to update all 20 (depending on what's actually changed/needed.)
Pros for the Custom View approach:
Central location for managing layout loading and control lookup
Can support backing code implicitly/internally for updating the view.
Can use attributes when being referenced in other layout files
Better encapsulation (you can hide the layout itself from the outside world exposing behaviors via direct methods)
Can simply be 'new'd' up in code and the layout will automatically load. No inflating or casting needed.
Cons for Custom View approach
When used in Layout files, you now have to also specify width and height making usage a little more verbose.
May add extra classes to your project (not always, but sometimes.)
Again, it seems to me like a no-brainer, but I'm wondering if there's a preferred 'Androidy' way, or is it just a preference up to each developer?
Such approach will add additional level in your view trees. So you only made the UI tree more complex for no good reason. From other side, you can follow next steps to get rid of that side-effect:
<merge /> can only be used as the root tag of an XML layout
when inflating a layout starting with a <merge />, you must specify a parent
ViewGroup and you must set attachToRoot to true (see the
documentation of the inflate() method)
That's it.
Well apparently what I came up with wasn't that unique after all! Here's an article explaining in detail this exact scenario, along with the pros and cons of each. Looks like they too came to the same conclusion.
TrickyAndroid: Protip. Inflating layout for your custom view

android studio: unique identifier for each element

I have an android application where not all elements have unique ID's. For example, two TextViews are each called "itemButton" and are on the same screen. I want to give every element a unique identifier by setting a tag on each element.
My current solution is to iterate through every element in the application and set the tag for each element. This is a very expensive solution because I have many elements. Is there another property you know of that would help identify an element other than setting a unique tag for each element?
View IDs have no special requirement that they be unique. However, you will run into difficulties if you use non-unique IDs on a single screen.
The two most common issues you will face if you use non-unique IDs are (1) failure of the system to automatically save instance state for the view and (2) findViewById() returning the "wrong" view.
Activity.findViewById() will search the view hierarchy for the first view it finds with the matching ID. If you have two views in your hierarchy with the same ID, that means you won't be able to find the second one using this method. However, you can use View.findViewById() instead.
View.findViewById() will search the view hierarchy starting from the view you're invoking the method on, which means that you can differentiate between two views with the same ID as long as they have different parents.
In your case, I suspect you can do something like the following:
View parentOne = findViewById(R.id.parentOne);
View childOne = parentOne.findViewById(R.id.someIdBeingReused);
View parentTwo = findViewById(R.id.parentTwo);
View childTwo = parentTwo.findViewById(R.id.someIdBeingReused);
I don't think you got it right, you should assign unique ids to your elements by using in each of them: android:id="#+id/YOUR_ID", then you can find them with findViewById(R.id.YOUR_ID), so, if you have two text views, lets say, username and password, you set the ids on each:
<TextView android:id="#+id/username" .../>
<TextView android:id="#+id/password" .../>
and then you get them in your activity or fragment with:
TextView txtUsername = findViewById(R.id.username);
TextView txtPassword = findViewById(R.id.password);

Querying root and indirect sibling views in Android ListView

I have a ListView to show a list of articles. Each ListView element is a LinearLayout. On each article, there is a TextView button to edit the title (another TextView). However, the button and the title are not under a direct parent (and actually not at the same level).
A sample structure is shown below:
<LinearLayout>
<LinearLayout
android:id="#+id/title_zone">
<TextView
android:id="#+id/title">
<ImageView
android:id="#+id/icon_popularity">
</LinearLayout><!--end of title_zone-->
<LinearLayout
android:id="#+id/content_zone">
...
</LinearLayout><!--end of content_zone-->
<LinearLayout
android:id="#+id/button_zone">
<LinearLayout
android:id="#+id/author_buttons">
<TextView
android:id="#+id/edit_title_button"
android:onClick="editTitle">
...
</LinearLayout><!--end of author_buttons-->
</LinearLayout><!--end of button_zone-->
</LinearLayout>
I write a SimpleAdapter to apply data to views, so the root LinearLayout will have a tag of the article ID. When editTitle() is called, it needs to find its parent's parent's parent to the root. And after new title is entered, a message will send to server containing new title and the article ID. Also, the title text will be changed visually, which means I need to find the title TextView based on the root.
The problem is that this querying root process is tightly coupled to the UI structure. If I changed the structure in XML, I need to pay attention to change the querying code in Java. (The querying title view is relatively easy, if the root is obtained.)
Is there a more maintainable way to implement my purpose?
OK. A solution that works for my situation is as follows:
In the customized simple adapter for this ListView, set the article ID as the edit_title_button's tag. And set the article ID composed string as the root LinearLayout's tag, e.g., "a_123" if the article ID is 123.
So, when editTitle(View v) is called, we can get the story ID by v.getTag(). Of course, we can also get the ListView by its ID (e.g., ListView list=findViewById(R.id.my_list_view);). Then, we can simply get the root LinearLayout by list.findViewWithTag("a_123").
Since the root LinearLayout is obtained without knowing the UI structure, following codes are easy to maintain.

Android splitview/region presenter with MvvmCross

My goal is to create an Android UI window that displays three regions: navigation, main, and popup. The navigation and main region will function as a split view. The popup region will be centered in the screen and overlay the other two regions; it will also only be visible part of the time. Ideally, I'd like these regions to host fragments that are dynamically changed to display different layouts and view models based on user interaction. And, all of this should be accomplished without breaking the MvvmCross bindings.
I've got something that works, but it feels a bit hacked together. The current implementation most closely follows resource 1 below. Each region has a dictionary. All of the fragments are registered with a dictionary based on their target region. This is done from the activity. The activity also takes care of inflating each layout and associating it with the correct view model. I’d like to change this so that MvvmCross can do more of the work.
Is it possible to create a custom presenter, in Android, that is a mix between the dynamic fragment layout from resource 2 and the iOS custom presenter shown in resource 3? Just to clarify, I want to specifically define where each region will be displayed using a layout. Then I want to dynamically fill the content, of each region, with different layouts and their associated view models, at runtime.
Resource 1: MvvmCross v3 Fragment Sample
https://github.com/slodge/MvvmCross-Tutorials/tree/master/Fragments
See: “FragmentSample.UI.Droid/Views/TitlesView.cs” and “FragmentSample.UI.Droid/Setup.cs”
Resource 2: N=26 - Androids… down at Fragment Rock
http://www.youtube.com/watch?feature=player_embedded&v=uQT3_WXQQr0
Dynamic Fragment Layout created explained at 26:25 – 32:10
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:id="#+id/subframe1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<FrameLayout
android:id="#+id/dubframe1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
(In addition to being an extremely helpful video, of all the N+1 videos I've had a chance to watch, this one has the best intro!)
Resource 3: N=24 - Split View
http://www.youtube.com/watch?feature=player_embedded&v=PpeysFIINcY
iOS SplitPresenter created at 11:25 – 15:05
public class SplitViewController : UISplitViewController
{
public SplitViewController()
{
this.ViewControllers = new UIViewController[]
{
new UIViewController(),
new UIViewController(),
};
}
public void SetLeft(UIViewController left)
{
this.ViewControllers = new UIViewController[]
{
left,
this.ViewControllers[1]
};
}
public void SetRight(UIViewController right)
{
this.ViewControllers = new UIViewController[]
{
this.ViewControllers[0],
right,
};
}
}
I created a small example project on GitHub that demonstrates how to create multiple regions.
This shows the three regions: Navigation, Main and Popup. The location, size and shape of each region is defined in one layout file. The content is defined, with separate layout files and view models, for each region and is dynamically changed at runtime. MvvmCross bindings still work with each individual view model.
EDIT
I added a more robust solution example on github. This allows the ViewModel to be opened in the standard way. The MultiRegionPresenter handles matching up the view with the correct region by looking at a tag in the view. Views are now tagged with their intended region like this: [Region(Resource.Id.MainRegion)].
The new example project is located here: MultiRegionPresenter Example

Android: how to use XML for custom view layout

I'm coming from the world of GWT and UIBinder, where I'm used to defining custom components by extending Composite, and then putting all the UI layout code in a MyComponent.ui.xml file. This is a very nice way to build up components from smaller pieces.
I'm trying to achieve the same effect on Android with custom Views. I've been able to programmatically extend View, and then add objects by calling addView(textView). I'd like to be able to do that in XML, but I don't see how to associate an xml layout file with the view (apart the primary res/layout/main.xml file, which provides the primary layout for the app.
How can I layout my custom views in XML?
Edit: My question was unclear. What I'm looking to do is associate a my_widget.xml file with my customized view. Then in my_widget.xml I'd like to define various TextViews, etc, and plug them into my View class.
Use the fully qualified name of your custom view class in place of one of the built-in views. Here's a layout, for instance, that fills the window with your class:
<?xml version="1.0" encoding="utf-8"?>
<my.package.MyCustomView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
To construct Views from XML, use LayoutInflater.
You can use findViewById (int id) with an id defined in XML. See the example in the android doc here (scroll down to IDs).

Categories

Resources